Sunday, 25 August 2019

Logging in AWS lambda

Problem :

Logs are very important aspect of any microservice or any AWS service and in AWS if your rate of generating Cloudwatch logs is high ,then  it can increase your AWS costs significantly. There should be some limit on the growth of the logs to keep costs under control.

Solution :

There are multiple ways to manage the logging. Let's begin with some of the best practices and then some framework level changes which can help in reducing the overall logging.


Best Practices :


1. Most of the times we are only interested to find error scenarios from the logs so it is important to use log.error while logging error cases. Never use other logging levels for logging errors

2. Never use print statements instead use a proper logging framework to log the statements

2. Be diligent about the log statements and categorize them correctly at different log levels : DEBUG, INFO, ERROR

4. Don't log data unless there is a critical need for same. Even if required, the data should be printed only at DEBUG level 



Framework level changes :


Apart from the best practices listed above, it is also required that we put some controls in place to keep a check on unwanted logging. Here are few services on which I have implemented logging framework .

  •  Lambda functions - Python/Java
  •  ECS services- in Java

It is important that we handle logging from both the services.
Idea is to keep the logging levels at minimum for all the development environments. So for most of the dev/test environments, logging will be kept at ERROR while UAT and Prod may run on INFO but can be increased to DEBUG temporarily for troubleshooting.





1. ECS services

Following steps should be taken to retrofit logging in all ECS components:

1. Add following properties in application.properties:
1
2
3
4
5
logging.level.root=${LOG_LEVEL}
LOG_LEVEL=INFO
#endpoints.loggers.sensitive=false
management.endpoints.web.exposure.include=health,info,loggers
org.springframework.boot:type=Endpoint,name=Loggers


2. Enable Spring Actuator framework by adding following dependencies in pom.xml:

pom.xml:
1
2
3
4
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>


3. Make following changes in CF template of the project :
1. Add security group - Below is a sample config. Rename the variables as per project name and usage :

Preview:
2. HelloComponentSecurityGroup:
3.   Type: AWS::EC2::SecurityGroup
4.   Properties:
5.     GroupName: !Sub "hello-component-${EnvironmentName}-sg"
6.     GroupDescription: ECS security group
7.     SecurityGroupIngress:
8.     - IpProtocol: tcp
9.       FromPort: 8080
10.       ToPort: 8080
11.       CidrIp: 10.0.0.0/8
12.     SecurityGroupEgress:
13.     - IpProtocol: tcp
14.       FromPort: 0
15.       ToPort: 1111
16.       CidrIp: 0.0.0.0/0
17.     VpcId:
18.       Fn::ImportValue: "vpc-id"
19.     Tags:
20.     - Key: Company
21.       Value: !Ref CompanyTag    
22.     - Key: StageName
23.       Value: !Ref StageName
24.     - Key: EnvironmentName
25.       Value: !Ref EnvironmentName
26.     - Key: Name
27.       Value: !Sub "hello-component-${EnvironmentName}-sg"
28. Make sure to include the security group in ECSService block:

29. EcsService:
30.   Type: 'AWS::ECS::Service'
31.   Properties:
32.     Cluster: !Sub ${ClusterName}-${EnvironmentName}
33.     ServiceName: !Sub "${ServiceName}-service"
34.     LaunchType: FARGATE
35.     DesiredCount: !Ref ServiceDesiredCount
36.     TaskDefinition: !Ref TaskDefinition
37.     DeploymentConfiguration:
38.       MaximumPercent: 200
39.       MinimumHealthyPercent: 100
40.     NetworkConfiguration:
41.       AwsvpcConfiguration:
42.         AssignPublicIp: DISABLED
43.         ############## Include the statements below #############
44.         SecurityGroups:
45.           - !Ref HelloComponentSecurityGroup

46. Add another Environment variable in TaskDefinition → Environment :
47. Environment:
48.   -
49.     Name: LOG_LEVEL

    Value: !Ref LogLevel ### (This LogLevel field should be defined in parameters and should be derived through config project)



2. Lambda Services

Let's try to define logging strategy for both Python and Java based Lambda functions:


2.1. Python based lambda

Python has inbuilt logging framework which can be used to log statements at different logging levels (much like in Java). Following steps can be used to enable the same:

1. import logging in main lambda function python file (file which has lambda_handler) and before beginning of the function write following statements:

import logging
logger = logging.getLogger()
logger.setLevel(os.environ['LOGGING_LEVEL'])


2. Use log.info, log.error or log.debug instead of context.log to log statements appropriately.



2.2. Java based Lambda

Unfortunately, for the lambda(s) built in Java, the standard context logging framework does not support different logging levels. So we will have to use aws-lambda-java-log4j framework to make logging inline with other compute resources.

1. Add following dependencies in pom.xml :

pom.xml:
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-log4j2</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
</dependency>


2.  Paste following file in src/main/resources/log4j.xml:

log4j.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="true"
 xmlns:log4j='http://jakarta.apache.org/log4j/'>

 <appender name="file" class="org.apache.log4j.RollingFileAppender">
    <param name="append" value="false" />
    <param name="maxFileSize" value="10KB" />
    <param name="maxBackupIndex" value="5" />
    <!-- For Tomcat -->
    <param name="file" value="${catalina.home}/logs/myStruts1App.log" />
    <layout class="org.apache.log4j.PatternLayout">
  <param name="ConversionPattern" 
   value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
    </layout>
 </appender>

 <root>
  <level value="ERROR" />
  <appender-ref ref="file" />
 </root>

</log4j:configuration>

We need to replace context.log statements to log.debug /log.info / log.error statements as applicable. But before we use log statements, we need to define the logger for the given class:

private  static  final  Logger log = LogManager.getLogger(Hello.class);
.... 
log.error("Error:  Exception processing input"  + e.getMessage(), e);







Friday, 23 August 2019

Extent report plugin for cucumber framework



Extent Reports are the most popular reporting used with Selenium. ExtentReport API makes our life easy to generate interactive report with simple configuartions. It supports almost all Java and .NET test frameworks such as TestNG, JUnit, NUnit etc

Here we are discussing about a plugin which is build on Extent Report specially for Cucumber.This plugin is used to simple out the implementation of Extent Report in Cucumber Framework


We are creating a maven project to implement the integration of our plugin with cucumber

1. Create new maven project in any tool eclipse/sts/intellij
2. Open pom.xml and update below entries.

Step 1 : Add Cucumber Extent Reporter library to Maven Project


Add cucumber-extentsreport
<dependency>
    <groupId>com.vimalselvam</groupId>
    <artifactId>cucumber-extentsreport</artifactId>
    <version>3.0.2</version>
</dependency>

Add below  dependencies:

   <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>1.2.5</version>
        </dependency>

        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-core</artifactId>
            <version>1.2.5</version>
        </dependency>



Note:  If you are using Java 8+,  add the dependency of ExtentReport v3.0.6 + as well.


Add Extent Report library
<dependency>
    <groupId>com.aventstack</groupId>
    <artifactId>extentreports</artifactId>
    <version>3.1.2</version>
</dependency>


Step 2 : Add Extent Config to the Project


We need to set few configurations which is required by the Cucumber Extent Report plugin. 

We can set configuration via XML configuration file.

1. Create New folder configs under src/test/resources/
2. Create a New File in folder config and name it as extent-config.xml.

Few properties explanation .

• Global Time Format  : <timeFormat> : Like this HH:mm:ss
• Title of the Report : <documentTitle> : This will display on the Browser Tab
• Document Encoding : <encoding> : UFT-8
• Global Date Format : <dateFormat> : Like this yyyy-MM-dd
• Report Theme : <theme> : standard or dark
• Name of the Report : <reportName> : This will display at the top of the Report


extent-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<extentreports>
  <configuration>
    <!-- report theme --> <!-- standard, dark -->
    <theme>standard</theme>
   
    <!-- document encoding -->  <!-- defaults to UTF-8 -->
    <encoding>UTF-8</encoding>
     
    <!-- protocol for script and stylesheets -->   <!-- defaults to https -->
    <protocol>https</protocol>
     
    <!-- title of the document -->
    <documentTitle>Cucumber Framework</documentTitle>
     
    <!-- report name - displayed at top-nav -->
    <reportName>My Project- Cucumber Report</reportName>
     
    <!-- global date format override -->  <!-- defaults to yyyy-MM-dd -->
    <dateFormat>yyyy-MM-dd</dateFormat>
     
    <!-- global time format override -->   <!-- defaults to HH:mm:ss -->
    <timeFormat>HH:mm:ss</timeFormat>
     
    <!-- custom javascript -->
    <scripts>
      <![CDATA[
        $(document).ready(function() {
         
        });
      ]]>
    </scripts>
     
    <!-- custom styles -->
    <styles>
      <![CDATA[
         
      ]]>
    </styles>
  </configuration>
</extentreports>



Step 3: Read the extent-config.xml path

1. Create a "Configuration.properties" file under src/test/resources/configs folder and 

2. Make an entry for the Path of the config in the Configuration.properties file.

Configuration.properties

reportConfigPath= C:/Users/vikas/my-project/src/test/resources/configs/extent-config.xml


Step 4: Create Reader class ConfigFileReader


ConfigFileReader.java:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

public class ConfigFileReader {
 private Properties properties;
 private final String propertyFilePath = "src//test//resources//configs//Configuration.properties";

 public ConfigFileReader() {
  BufferedReader reader;
  try {
   reader = new BufferedReader(new FileReader(propertyFilePath));
   properties = new Properties();
   try {
    properties.load(reader);
    reader.close();
   } catch (IOException e) {
    e.printStackTrace();
   }
  } catch (FileNotFoundException e) {
   e.printStackTrace();
   throw new RuntimeException("Configuration.properties not found at " + propertyFilePath);
  }
 }

 public String getReportConfigPath() {
  String reportConfigPath = properties.getProperty("reportConfigPath");
  if (reportConfigPath != null) return reportConfigPath;
  else throw new RuntimeException("Report Config Path not specified in the Configuration.properties");
 }
}



Step 4 :  Modify TestRunner to Implement Cucumber Extent Reporter


1. Modify the runner class with below plugin using 


@CucumberOptions( plugin = {com.cucumber.listener.ExtentCucumberFormatter:target/cucumber-reports/report.html})

The above setup will generate the report in taget directory with the name of report.html.


2. Write Extent Reports

Add a method createExtentReport() in the TestRunner class to write the report.This will load the extent configurations from extent-config.xml and write the report in the desired path.

TestCaseRunner.java:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.home.runners;

import com.cucumber.listener.Reporter;
import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
import managers.FileReaderManager;
import org.junit.AfterClass;
import org.junit.runner.RunWith;
import java.io.File;

@RunWith(value = Cucumber.class)
@CucumberOptions(features = "src/test/resources/features/myfirstRequirement.feature",
    glue = {
        "com.home.stepDefinitions"
    },
    plugin = {
        "pretty",
        "com.cucumber.listener.ExtentCucumberFormatter:target/cucumber-reports/report.html"
    },
    tags = {
        "@functional"
    }
)
public class TestCaseRunner {

    @AfterClass
    public static void createExtentReport() {
        Reporter.loadXMLConfig(new File(FileReaderManager.getInstance().getConfigReader().getReportConfigPath()));
        Reporter.setSystemInfo("User Name", System.getProperty("user.name"));
        Reporter.setSystemInfo("Time Zone", System.getProperty("user.timezone"));
        Reporter.setSystemInfo("Machine", "Windows 7:-" + "64 Bit");
        Reporter.setSystemInfo("Maven", "3.5.2");
        Reporter.setSystemInfo("Java Version", "1.8.0_151");
        Reporter.assignAuthor("Vikash");
    }
}


If you noted above we have used FileReaderManager class to get the instance of ConfigFileReader class, so below is the imlementation of the same.


FileReadManager.java:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package managers;

import dataProviders.ConfigFileReader;

public class FileReaderManager {
 private static FileReaderManager fileReaderManager = new FileReaderManager();
 private static ConfigFileReader configFileReader;

 private FileReaderManager() {}

 public static FileReaderManager getInstance() {
  return fileReaderManager;
 }

 public ConfigFileReader getConfigReader() {
  return (configFileReader == null) ? new ConfigFileReader() : configFileReader;
 }
}


Step 5: Run as JUnit


Now we are all set to run the Cucumber test. 

1. Right Click on TestRunner class 

2. Click Run As  >> JUnit Test. 

3. Find the report at target\cucumber-reports\report.html.




Thursday, 15 August 2019

Guidelines for Testing

Below are few guidelines which can be followed by testing team while working in a CI-CD environment. 


Install Android studio for ionic in Linux

Step by Step guide for installing android studio on linux machine and to debug your application on device.


Step 1: install openjdk

sudo apt-get install openjdk-7-jdk


Step 2:  install `android sdk`

wget http://dl.google.com/android/android-sdk_r24.2-linux.tgz
 
  tar -xvf android-sdk_r24.2-linux.tgz

  cd android-sdk-linux/tools

  # install all sdk packages
  ./android update sdk --no-ui


Step 3. Set path 

set PATH in `~/.bashrc`

export PATH=${PATH}:$HOME/Android/Sdk/platform-tools:$HOME/Android/Sdk/tools:$HOME/Android/Sdk/build-tools/23.0.2/


Step 4. install the desired Android platforms

/home/razor/Android/Sdk/tools/android sdk


Step 5. install Android images

/home/razor/Android/Sdk/tools/android avd


Step 6. add Android platform to Ionic

ionic add platform android


Step 7. Run emulator

ionic emulate android


Step 8. Enable your phone for development mode

  go to `Settings > More > About` and tab `Build number` 7 times

  go to `Settings > More > Developer Options` and enable `USB debugging`

 
Step 9. Run Ionic app on Android phone natively

  connect Android phone to PC via USB

  run `ionic run android`

 
Step 10. Debug the app running on the phone

with the app running on the device open Chrome and navigate to `chrome://inspect/#devices` . find your device and click `Inspect`




Spring boot with CORS

CORS (Cross-Origin Resource Sharing) errors occur when a web application running in a browser requests a resource from a different domain or...