I used to use my a custom class that is responsible for logging with the following implementation
public class Logger {
public final static boolean DEBUG = true;
public static void d(String tag, String msg) {
if(DEBUG)
Log.d(tag, msg);
}
}
When I release the app I set the flag to false, however I was said that the following method is not that efficient since the Strings are still being allocated and then deallocated in the memory
the logger being used like that:
Logger.d("tag", "message");
or maybe like that, by doing that the StringBuilder will be invoked
Logger.d("tag", "server response: " + response + " timing: " + timing);
Is that true, can't the dalvik/compiler optimize it to prevent such behaviour. and if so, is there something I can do to optimize it other than moving the if outside?
Try to use Proguard to entirely remove logging from bytecode of your production apk. On debug version just disable "proguarding".
E.g.:
Remove all debug logging calls before publishing: are there tools to do this?
By the way, few unnecesary string concatenations are not good, but usually are not that hard (unless you are doing it many times in the loop), so don't demonize it :)
A simple solution would be to just just declare two String variables in your Activity and reuse them whenever you add a log entry like so:
public class YourActivity extends Activity
{
String tag;
String message;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Rest of Activity
Then whenever you want to add a log entry simply set the tag/message value:
tag = "myTag";
message = "myMessage";
Logger.d(tag, message);
This way your if check remains inside Logger and no additional memory is allocated.
Related
I'm searching for a concept to forward an object to subobjects.
Example:
I would like to create log files for several main Objects, that include sub objects (imagine a REST server that would log every single connection by ID).
Creating one big log file is simple ( redirect System.out.println, I already encapsulated that)
Example code:
class SubElementA{
public SubElementA(){
Debugger.debug("I am called, too");
}
}
Application.java
package com.dev4ag;
class Application{
private ElementA elA;
private String prefix;
public Application(String name){
this.elA = new ElementA();
this.prefix = name;
}
public void countUp(){
Debugger.debug(this.prefix+": I will now count up");
this.elA.doSomeStuff();
}
}
ElementA.java
package com.dev4ag;
class ElementA{
private int counter;
private SubElementA subElementA;
public void doSomeStuff(){
counter++;
Debugger.debug("Counter is: "+counter);
}
//Constructor
public ElementA(){
subElementA = new SubElementA();
this.counter = 0;
};
}
SubElementA.java
package com.dev4ag;
class SubElementA{
public SubElementA(){
Debugger.debug("I am called, too");
}
}
Debugger.java
package com.dev4ag;
public class Debugger {
public static void debug(String output){
//Just imagine we would write to a file here ;)
System.out.println(output);
}
}
(it was more easy to write system.out.println than to create a file, just imagine, Debugger.debug would write to a file).
Now I am thinking about a solution to create one Debug output target for each App. I could definitely change debug to not being static and create a debug object within Application.
But is there any way to use this object in the sub classes without forwarding the debug object either through Constructor or setter function, which would mean to have to add an object for the debugger to each class?
What would be the most beautiful solution for that?
Note that this solution might decrease performance a lot and it is pretty dirty way, but some loggers include such data.
But you can use Thread.currentThread().getStackTrace() to get stacktrace like in error and get class and method from where your method was called.
If you are using java9+ then you should probably use StackWalker API instead, especially that it have nice filters and other useful features.
So then you could guess app by class/method names on the stack.
We are using the Ereza CustomActivityOnCrash library to handle unexpected issues with our android app. It's activity offers some debug output which we enable in develop and test builds but disable on production builds. In addition we want to log that information about crash details (log entries, stack trace, error details) in background.
CustomActivityOnCrash offers to call event listeners which sounds convenient. I wanted to implement the interface in our logging service, however I do not understand how I can access the existing information in the crash activity that way. Which puzzles me, cause isn't that a natural expectation?
Basically I need to access public methods of an android activity object from an event listener method that does not get handed over anything. How can I access that activity in the handler method? And how can I get the intent of the activity leading to the crash which is the argument the crash activity expects in those public methods it offers to access the existing information it offers? The examples given in the libraries documentation and those I could find on the internet are trivial, they only dump example strings, not the actual data collected by the library.
This all sounds counter intuitive to me. Which is why I think I generally miss something here. Maybe someone has a short hint for me to bring me on track again. Thanks!
Here is the basics of the LogService implementation I imagine:
...
import cat.ereza.customactivityoncrash.CustomActivityOnCrash;
...
public class LogService
implements CustomActivityOnCrash.EventListener {
private static LogService instance;
...
public void log(LogLevel level, String message) {
....
}
public void logCrashDetails(String activityLog, String stackTrace, String errorDetails) {
String message = String.format(
"--- CRASH REPORT ---\n\n-- Activity log:\n%s\n\n- Stack trace:\n%s\n\nError details:\n%s",
activityLog,
stackTrace,
errorDetails);
log(LogLevel.ERROR, message);
}
....
// CustomActivityOnCrash EventListener interface
#Override
public void onLaunchErrorActivity() {
log(LogLevel.INFO, "COAC: app crashed");
logCrashDetails(
// CustomActivityOnCrash.getActivityLogFromIntent(...some intent...),
// CustomActivityOnCrash.getStackTraceFromIntent(...some intent...),
// CustomActivityOnCrash.getAllErrorDetailsFromIntent(...some intent...)
);
}
#Override
public void onRestartAppFromErrorActivity() {
log(LogLevel.INFO, "COAC: app restarted");
}
#Override
public void onCloseAppFromErrorActivity() {
log(LogLevel.INFO, "COAC: app closed");
}
}
Suppose I have a program with 20 classes, each with private static Logger logger = LoggerFactory.getLogger(SomeClass.class);. These 20 classes will log warn, info, debug, or error.
Within one of the classes, how can I can access to all the errors logged in the program thus far (across all of the classes)? Note I'm not interested in dumping all these to a .log, I want access to them from within the Java program.
Use a custom appender to add to list which is singleton list using that you can access all the mismatch.
CustomAppender.java
public class CustomAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
#Override
protected void append(ILoggingEvent eventObject) {
if(eventObject.getLevel() == Level.WARN){
{
KPLogHolder.addData(eventObject.getMessage);
}
}
}
A singleton java class to hold messages KPLogHolder.java
public class KPLogHolder {
private static List<String> holder;
public static List<String> getLog(){
if(holder== null){
return new ArrayList<String>();
}
return holder;
}
public static void addData(String item){
getLog().add(item);
}
}
And accessing the mssage.
public class KPTest{
#Test
public void testLog(){
LOG.warn("Warning!!!!");
for(String str: KPLogHolder.getLog()){
System.out.println(str);
}
}
}
Note I've not used synchronization you need to implement those as ArrayList is not synced. Not sure why this requirement but be careful. most cases these goes to wrong implementation, there situation were it will bring down your server. if its not implemented properly.
Do you want to access it when all classes have logged the statement in the .log file or you want to keep accessing as and when the logging is happening.
If you want to access all the logs from those 20 classes after the logging funcation then why not read the .log file later in your other java program to access all the logs.
But i guess you need an indicator in your log file to determine those statementa have been logged by those 20 classes. say for example some string in the consoleappender configuration
Not very difficult -
Either
Read log file
or
Overrider your log4j framework so instead of logging to file, it should log data to java objects (this is going to be very memory intensive).
I would like to implement system of callbacks which looks like this (pseudo code):
final Listener listener = ListenerCtrl.addListener(new Listener() {
void onNotify(String response){
ListenerCtrl.unsetListener(listener);
} }
This code mean that after received message, i want to unscribe from future notifications. I found very attractive have this action inside of callback.
Here is my actual implementation:
final WebServiceMsgListener wml = new WebServiceMsgListener()
{
public void onMsgNotify(JSONObject response, int ecode)
{
Log.v(TAG, "getSetStateProgressBar MSG_MGT_STATICINFO: onMsgNotify ecode" +
ecode);
authDelegate.unsetMsgListener(wml);
}
};
authDelegate.addMsgListener(NAOMsg.MSG_MGT_STATICINFO, wml);
Unfortunately, my current implementation show me eclipse error:"The local variable wml may not have been initialized"
Question: how I can get round this, to finally unscribe inside of callback and dont have this error ?
Change your code to:
authDelegate.unsetMsgListener(this);
this refers to the current object (whose onMsgNotify() is being executed at the time this statement is executed).
Note: Although, the variable wml is available to the new object, it has not yet been initialized at the time of the creation of the object, hence the error. It is initialized right after the object is fully created.
This code:
public class MyActivity extends Activity {
private final boolean logging = getResources().getBoolean(R.bool.logging);
#Override
public void onCreate(Bundle savedInstanceState) {
if(logging) Log.d("my_log", "some text here");
// some onCreate code...
}
}
generates NullPointerException.
But this one:
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
final boolean logging = getResources().getBoolean(R.bool.logging);
if(logging) Log.d("my_log", "some text here");
// some onCreate code...
}
}
Does not.
The main idea to switch logging in entire application with a boolean resource.
I can successfully declare this variable for every function in class, but can it be done for entire class just once?
Have you considered using a proper logging framework?
If you use the slf4j API you can write stuff like
log.debug("A={}, B={}", a, b)
where the switch is externally set in a well-documented way whether to generate a log statement or not. Also the slf4j {}-construct allows delaying the call to a.toString() and b.toString() until after the logging framework has decided that the log message actually needs to be generated.
slf4j is an API. You have several backends to choose from. For starters you can just pick the "simple" backend.
See http://slf4j.org/manual.html for an introduction.
Instead of putting value to string resource, I used static variable in special class.
public class constants {
public static final boolean logging = true;
}
so it can be accessed from any activity:
private boolean logging = constants.logging;