Saturday, 6 January 2018

Writing your first Django app - PART 1

Background

Django is a python based web framework that let's you create webapps quickly and with less code. It's free and opensource. For more details on the framework itself visit -
In this post we will create a sample app in Django python framework.


Setup

This tutorial assumes you are using Django 2.0, which supports Python 3.4 and later.

Install python3 and pip3 -
  • sudo apt-get install python3-pip
Next install Django python framework using pip3 -
  • sudo pip3 install Django 
You can see the installed version of python and django in various ways. Some are given in screenshot below -


Creating a Django project

Create a skeleton of your Django app using following command -
  • django-admin startproject djangodemo
You should see a directory getting created with name djangodemo. Inside this you should have manage.py file and another directory with same name djangodemo. This inner directory named djangodemo is actually a python package. Outer directory is just a holder with manage.py file. manage.py file is used to give you command line tasks to interact with your django project. You can see the version of you django framework used with following command -
  •  python3 manage.py version 



Directory structure is as follows -



 Some other pointers other than ones mentioned above -
  • __init__.py tells python this directory should be considered as a package.
  • This also means your inner djangodemo directory is a python package.
  • settings.py: Your Django app settings go here.
  • urls.py: URLs used in your Django project go here.
  • wsgi.py: This is an entry-point for WSGI-compatible web servers that can serve your project.
 Now that you have created your project let's run it with following command -
  • python3 manage.py runserver


Ignore the warnings for now.

NOTE : You don't have to restart the server everytime you make changes to code. Django handles it. Just refresh the pages.

Open -
 You should see installation successful message as follows -



NOTE : By default your server will run on port 8000. But you can change it as follows -
  • python manage.py runserver 8080

Creating Django App

A project is collection of apps and it's configurations needed for a website to run. Apps are modules that run in your project. A project can have multiple apps. Similarly a app can be part of multiuple projects. Apps can be at any python paths.

You can create a app as follows -
  • python3 manage.py startapp testapp
I like to put all apps in a directory called apps in your actual python package directory. You can do that as follows -



Creating your webpage

Go to your apps directory and edit view.py to add following content -

from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello world!")


 Next in the same directory create a file called urls.py and add following content to it -



from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]


Finally go to your project directory  - djangodemo/djangodemo and edit urls.py file to have following content -

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('test/', include('djangodemo.apps.testapp.urls')),
    path('admin/', admin.site.urls),
]


Next in apps directory inside djangodemo directory create a file called __init__.py. You can do this using -
  • touch __init__.py
Now simply run your server and visit -
to see your site.


Understanding : First we created an app called testapp. It should have some default files like view.py. view.py stores all your views. Here we added a new view called index and mapped it inside a urls.py file to the root url ("") at the app level. Next we mapped this to urls.py at our project level for '/test'. include maps the url provided and forwards rest the included module. In this case it will check url has 'test/' and forward the rest which is - "" to the urls.py in the testapp where we have mapped request view to "". So request view gets rendered.

NOTE : Note how we added a __init__.py file in apps directory. This is to ensure python recognizes this directory as a package. So that we could use djangodemo.apps.testapp.urls in the urls.py of project.

That's it you created your 1st django project and app. We will see some more details about this in next post. Thanks.

Related Links


Thursday, 28 December 2017

How to enable logging in your Jira plugin

Background

In one of our earlier post we saw how to create a Jira cloud plugin -
You can similarly create a plugin for Jira server. Though these tutorials can help you develop the plugin what I felt inadequate is documentation around logging. Logging is very important in any code you write. It helps you understand the flow and find the issues in case it arises. In this post we will see how logging works for Jira.


How to enable logging in your Jira plugin

Jira uses log4j for runtime logging so you do not have to do anything out of the box to set the logging framework. If you are using atlassian sdk you can straight away start using slf4j logging in your code (It will use log4j underneath). A sample example could be -

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyClass {
    private static final Logger log = LoggerFactory.getLogger(MyClass.class);

    public void myMethod() {
        ...
        log.info("Log a message here");
        ...
    }
}


And that's it you can see those logs in your log file. Log file is located at following location -
  • <your_addon_dir>/target/jira/home/log
You should see multiple log files like -

-rw-rw-r--  1 athakur athakur       0 Dec 27 14:31 atlassian-greenhopper.log
-rw-rw-r--  1 athakur athakur 1248201 Dec 29 00:40 atlassian-jira.log
-rw-rw-r--  1 athakur athakur    2558 Dec 29 00:39 atlassian-jira-security.log
-rw-rw-r--  1 athakur athakur     223 Dec 27 14:33 atlassian-jira-slow-queries.log
-rw-rw-r--  1 athakur athakur   18217 Dec 28 18:39 atlassian-servicedesk.log



Jira plugin logs should be part of  atlassian-jira.log file.


Logging levels

There are five logging levels available in log4j: 

'DEBUG', 'INFO', 'WARN', 'ERROR' and 'FATAL'. Each logging level provides more logging information that the level before it:
  • 'DEBUG'
  • 'INFO'
  • 'WARN'
  • 'ERROR'
  • 'FATAL'
'DEBUG' provides the most verbose logging and 'FATAL' provides the least verbose logging. The default level is WARN, meaning warnings and errors are displayed. Sometimes it is useful to adjust this level to see more detail.

You can see these configurations in a file called log4j.properties. Localtion of this file is -
  • <your-addon-dir>/target/jira/webapp/WEB-INF/classes/log4j.properties
In this file you should see a line saying -

# To turn more verbose logging on - change "WARN" to "DEBUG"
log4j.rootLogger=WARN, console, filelog, cloudAppender
Just change the WARN to DEBUG if you want to see debug logs.


Alternatively you can temporarily change the debug level or add a new log level for a particular package using Jira admin console. Go to System -> Logging and profiling




Here you can see default logger set to warn. You can change this to debug or add a new package with corresponding log level.





Again note changes done from this admin console are temporary and do not persist over server restarts. To make permanent changes you need to edit file -
<your-addon-dir>/target/jira/webapp/WEB-INF/classes/log4j.properties




Related Links

Friday, 22 December 2017

Why use slf4j over log4j or logback for logging in Java

Background

In last post we saw how we can use slf4j over log4j and logback -
 But the question is why would be use slf4j over any logging implementation and not use the actual implementation. In this post we will try to understand this question.




 SLF4J or Simple logging Facade is not really a logging implementation but an abstraction that can use any of the logging implementation like -
  • java.util.logging, 
  • Apache log4j, 
  • logback etc
So consider this - You have developed a project that uses log4j for logging. But your project is dependent on some other module/library that uses lets say logback for logging. In this case you will need to include logback jar in your application as well. This is just unnecessary overhead. If the module your project is dependent on used slf4j then it could have reused our existing log4j configurations and jar.

Other way to see this is let's say you are writing a library that you want someone else to use. In this case you can use slf4j and let the user of your library choose the actual logging implementation rather than using a actual logging implementation like log4j and  making the user of your library stick to the same.


In short slf4j makes your code independent of any logging implementation specially if your code is part of public api/library.

Now that we know the very basics of why one would use slf4j lets see some of it's advantages -


Why use slf4j over log4j or logback for logging in Java

Let us see how a log statement would look in a log4j implementation -


if (logger.isDebugEnabled()) {
    logger.debug("Inputs are input1 : " + input1 + " input2 : " + input2 );
}


Couple of quick observations -
  1. Lot of boiler plate. Need to check if debug level is enabled everytime we need to log a debug statement.
  2. Lot of string concatenation everytime we call this debug statement.
In slf4j it would be as simple as -

logger.debug("Inputs are input1 : {} , input2 : {}" , input1, input2 );

Here {} are the palceholders and are replaced by the comma separated arguments provided later in the call. Yes the method takes variable arguments. And this is cool because - No more string concatenations!

You also avoid the boiler plate code since sl4fj will internally take care of the logging levels and proceed only if debug level is enabled. So if debug is not enabled final string needed to be logged is not even created. This not only help save memory but also CPU.

Related Links

How to configure Slf4j logging in your web application with log4j or logback implementations

Background

One of the important part of any application building is to implement proper logging.  Slf4j is widely used for this. Slf4j itself is not a logging implementation but it is kind of a wrapper over existing implementations like log4j etc. In this post we will see how we can configure our web application to use slf4j logging with -
  1. log4j
  2. logback
 For log4j I am going to use ivy as dependencies management tool and in case for logback I will use maven. But you can use any really as long as you include correct dependencies in your application.

Using slf4j with log4j

 To use log4j you need to include following dependencies in your application. Your ivy.xml file would look like -

<ivy-module version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
    <info
        organisation="osfg"
        module="WebDynamo"
        status="integration">
    </info>
    
    <dependencies>
        <dependency org="org.slf4j" name="slf4j-api" rev="1.7.21"/>
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
        <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.7.21"/>
    </dependencies>
</ivy-module>


You can see the complete xml here - https://github.com/aniket91/WebDynamo/blob/master/ivy.xml 
and complete working app here - https://github.com/aniket91/WebDynamo

Once you have dependencies in place you need to give it a configuration file to tell how your logging behaves. A sample configuration file would look like -

<?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="CONSOLE" class="org.apache.log4j.ConsoleAppender">
        <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>

    <appender name="FILE" class="org.apache.log4j.RollingFileAppender">
        <param name="append" value="false" />
        <param name="maxFileSize" value="10MB" />
        <param name="maxBackupIndex" value="10" />
        <param name="file" value="${catalina.home}/logs/webdynamo.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>
    
    <category name="org.springframework">
        <priority value="debug" />
    </category>

    <category name="org.springframework.beans">
        <priority value="debug" />
    </category>

    <category name="org.springframework.security">
        <priority value="debug" />
    </category>

    <root>
        <level value="DEBUG" />
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>

</log4j:configuration>

Again you can see this file in the same project mentioned above - https://github.com/aniket91/WebDynamo/blob/master/src/log4j.xml

This configuration file should be in the classpath. log4j implementation by default looks for a file called log4j.properties or log4j.xml in your classpath.

You can visualize this with following diagram -



Application code uses slf4j interface which in turn uses a log4j-slf4j bridge to talk to log4j implementation.

We will see how to actually use Sl4fj logger a bit later in this post. Let's look how to do the same with a logback implementation.



Using slf4j with logback

For this you need to add following dependencies. Your pom.xml dependencies section would look like -

            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>1.0.13</version>
            </dependency>

NOTE : you do not need slf4j-api here as logback has it as a compile time dependency. You can see that here - https://mvnrepository.com/artifact/ch.qos.logback/logback-classic/1.0.13.

NOTE : The logback-classic module can be assimilated to a significantly improved version of log4j. Moreover, logback-classic natively implements the SLF4J API so that you can readily switch back and forth between logback and other logging frameworks such as log4j or java.util.logging (JUL).  (Source)

You can visualize this with following diagram -




As we saw in log4j implementation we need to supply a configuration file to the implementation to tell how logging should work. In case of logback it expects a file called logback.xml to be in the classpath. A sample file could be -

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</Pattern>
        </layout>
    </appender>
    
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${catalina.home}/logs/springdemo.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>springdemo.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy
            class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>10</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%date{HH:mm:ss.SSS} %-5p [%t] %c{1} - %m%n</pattern>
        </encoder>
        <append>true</append>
    </appender>    
    
    <root level="DEBUG">
        <appender-ref ref="STDOUT" />
    </root>

    <logger name="com.osfg" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
    </logger>
</configuration>




This is in the same app memtioned above. You can see this file here - https://github.com/aniket91/SpringFeaturesDemo/blob/master/src/logback.xml


And that's it your logging framework is all set to be used. We will now see how we can actually use this logger.


Using slf4j logger in your application

 Following is a simple controller that uses slf4j logging (implementation can be anything underneath - log4j, logback etc)

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
 * 
 * @author athakur
 * Test controller
 */
@Controller
public class TestController {
    
    Logger logger = LoggerFactory.getLogger(TestController.class);

    @RequestMapping(value="/test/{data}",method=RequestMethod.GET)
    public String test(@PathVariable String data, ModelMap model,
            HttpServletRequest request, HttpServletResponse response) {
        logger.debug("Received request for test controller with data : {}", data);
        model.put("adminName", properties.getAdminName());
        return "test";    
    }
}

You can just run the code and see that logging works. This is again part of the same maven app I mentioned above in case of logback implementation. You can see this file here - https://github.com/aniket91/SpringFeaturesDemo/blob/master/src/com/osfg/controllers/TestController.java





Related Links

Friday, 15 December 2017

Fixing 'Error:Unsupported method: BaseConfig.getApplicationIdSuffix()' issue in Android Studio

Background

I tried importing an old android project into my Android Studio and the build was failing with following error -

Error:Unsupported method: BaseConfig.getApplicationIdSuffix().
The version of Gradle you connect to does not support that method.
To resolve the problem you can change/upgrade the target version of Gradle you connect to.
Alternatively, you can ignore this exception and read other information from the model.




Solution

As the error itself says solution is to upgrade gradle version.

Go to project level build.gradle. For me it looks like below -

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.2.3'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}


Just upgrade the gradle version from 1.2.3 (or whatever you have)  to 3.0.1. You can try upgrading to other higher versions as well. This is what worked for me. So the build.gradle looks like below -

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}




Once you do that click on "Try again" and the gradle sync should go through.



t> UA-39527780-1 back to top