Back to project page BestBoard.
The source code is released under:
MIT License
If you think the Android project BestBoard listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package digitalgarden.magicmerlin.scribe; /* www . j av a 2 s . c om*/ import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.RandomAccessFile; import java.lang.Thread.UncaughtExceptionHandler; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import android.content.Context; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.widget.Toast; /** * Alternatv log. * <p> * Az osztly statikus metdusokat tartalmaz, melyek a log zenetek teljes kirst nmagukban elvgzik. * Az {@code AndroidManifest.xml}-ben engedlyek megadsa szksges: * {@code <uses-permission * android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> } * Az {@code android.permission.READ_LOGS} megadsa tbb nem szksges. * <p> * Hrom irnyban kpes log zenetet kldeni: * <ul> * <li>system log</li> * <li>file log</li> * <li>toast log (alpertelmezetten kikapcsolva)</li> * </ul> * A log teljes egszben ill. az egyes irnyok egyenknt ki- ill. bekapcsolhatak. * A system log alatti tag bellthat. * A file log esetben a directory path, ill. a file neve megadhat, ill. a teljes file trlhet? is. * Amennyiben a file log hossza a belltott rtket meghaladja, a file log tbb lpcs?ben archivlhat. * <p> * Ha a program egy rszben specilis belltsokat szeretnnk, * akkor a fenti belltsok egyben elmenthet?ek s visszatlthet?ek. * <p> * A debug zenetek "limitlhatak": minden zenethez hozzrendelhetnk egy szint (level) rtket. * Ezek az zenetek csak akkor kerlnek elkldsre, ha a szint (level) rtkk a belltott szls?rtkek kz esik. * (A NO_LIMIT rtk feletti zenetekre ez a megszorts nem vonatkozik.) * rdemes egy adott osztlyban egyforma, de alacsony szint? osztlyoknl alacsony, * mg fels?bb m?kdst vgz? osztlyoknl magasabb rtket belltani. Ezzel elrhet?, hogy a fels? szint? * vezrlst az als szint korltozsval ellen?rizzk (vagy fordtva). * Korltozhatjuk az ellen?rzst akr egyetlen osztlyra is. * <p> * Az zenetek tpusai: * <ul> * <li>TITLE - cm (log file hossz ellen?rzs is!)</li> * <li>NOTE - norml zenet</li> * <li>ERROR - hibazenet</li> * <li>DEBUG - norml zenet - csak debug log esetn!</li> * <li>LOCUS - full.class.method (thread) formban a pozci</li> * </ul> * A DEBUG (s vele egytt a LOCUS) tpus log zenet kln engedlyezhet?/letilthat. * <p> * A log zenetek konfigurlsa: * Kt kln konfigurci llthat be, a pros s pratlan szintek (level) konfigurcija eltrhet. * A msodlagos (pratlan szint) konfigurci csak akkor kerl bekapcsolsra, ha annak valamelyik rtkt megvltoztattuk, * addig a kt konfigurci azonos. A msodlagos konfigurcival m?kd? parancsok a 'Secondary' kiegsztst kaptak, * ill. a pratlan szint? zenetek hasznljk. * Msodlagos konfigurcit hasznlhatjuk pl. a felhasznlt/fejleszt?t kln clz log belltsra, * vagy ha egy folyamatot a f? belltsoknl rszletesebben akarunk megfigyelni. * <p> * Log zenetet brmely szlrl kldhetnk. * A konfigurcik belltsa is 'thread-safe', azonban ezek a belltsok azonnal tkrz?dnek a tbbi szlon, * ezrt clszer? csak main-szlon (mg a tbbi thread indtsa el?tt) elvgezni a belltsokat. * <p> * A belltsok statikus vltozkban (azaz application szinten) kerlnek trolsra. Figyelni kell arra, * hogy rtkket az jraindtsok kztt meg?rizhetik. rdemes ezrt a program belpsi pontjain * a konfigurct(kat) inicializlni: * {@link #init(Context)} * Ilyenkor az alaprtelmezett log file neve a package nvvel megegyezik; * vagyis a klnbz? programjaink elklnlten jegyzik fel a log zeneteket. * <p> * A 'system-log' kezelse is lehetsges: * A system log zeneteket tmsolhatjuk a file-logra (els?dleges konfigurci), vagy trlhetjk. * API 16 felett a system log-bl csak a sajt programunk log zenetei olvashatak ki. * <p> * Lehetsges az el nem kapott kivtelek file-logra kldse is (primr konfigurci). * A log file-ban a kivtelek fordtott sorrendben szerepelnek: * azaz a kivtel-kaszkdot indt kivtel ll az els? helyen. * A stack-trace teljes egszben kirsra kerl, nincs rvidts. * A kirst kvet?en a vezrlst a rendszer visszakapja, * gy a system-log-ra az android rendszer rja ki a hibazeneteket, * valamint a programot is le fogja lltani. */ public class Scribe { /************************************************************ * * * Default constant values * * * ************************************************************/ /** Log file's default extension */ public static final String DEFAULT_FILE_EXT = ".log"; /** Log file's default name */ public static final String DEFAULT_FILE_NAME = "scribe" + DEFAULT_FILE_EXT; /** Log file's default directory */ public static final String DEFAULT_DIRECTORY = ""; /** Default tag for system log */ public static final String DEFAULT_LOG_TAG = "SCRIBE"; /** Log file will be archived above this size */ public static final long MAX_LOGFILE_LENGTH = 1024*1024; /** Maximum number of archived log files */ public static final int MAX_LOGFILE = 8; /** Debug cannot be limited above this value */ public static final int NO_LIMIT = 1000; /** Not limited level value for secondary config */ public static final int NO_LIMIT_SECONDARY = NO_LIMIT + 1; // Constants returned by methods /** Returned value: everything was OK */ public static final String OK = "Log ready"; /** Returned value: logging is disabled */ public static final String OFF = "Log disabled"; /** Logging error: returned value starts with this */ public static final String LOGFILE_ERROR = "LOGFILE ERROR: "; /** Path separator */ private static final String SEPARATOR = System.getProperty("line.separator"); /** Primary config */ private static final int PRIMARY_CONFIG = 0; /** Secondary config */ private static final int SECONDARY_CONFIG = 1; /************************************************************ * * * Private config variables * * * ************************************************************/ /** Config settings, organised in one nested class */ private static class Config { /** Is logging enabled? (Main switch) */ private boolean enabled; /** Is debug-logging enabled? */ private boolean debugEnabled; /** System-log tag. Null disables */ private String logTag; /** File-log file name. Null disables */ private String fileName; /** File-log path (on sd-card). Cannot be null */ private String directoryName; /** Toast-log context (of Activity). Null disables. */ private Context context; /** Message levels below this limit are disabled */ private int minLimit; /** Message levels above this limit are disabled. No restriction above NO_LIMIT! */ private int maxLimit; /** Is time stamp enabled for file-log messages? */ private boolean timeStampEnabled; /** Is space stamp (class.method) enabled for file-log messages? */ private boolean spaceStampEnabled; } /** Default log file name: could be changed to package.extension */ private static volatile String defaultFileName = DEFAULT_FILE_NAME; /** Temporary variable to set up config[] first */ private static final Config primaryConfig = new Config(); /** * Settings for the two configurations. * Reference of the array is final; second element can be changed to secondaryConfig. * Before its activation secondary config points to the same config as primary config. */ private static final Config config[] = { primaryConfig, primaryConfig }; /** Separate locks for accessing the two configurations */ private static final Object[] lock = { new Object(), new Object() }; // Both config[0] and [1] point to primaryConfig. But only config[0] could be changed // (locked by lock[0]). Writing config[1] means activation of secondary config, so it // will not point to primaryConfig any more. config[1] is locked by lock[1]. /** Fill up default values at first start */ static { resetAll(PRIMARY_CONFIG); } /************************************************************ * * * Synchronized private methods for configurations * * * * 'config' array is final, could not change * * 'config[0]' will always point to primary config * * 'config[1]' will point to primary config, then * * to secondary config (after activation) * * 'lock[0]' will lock config[0] and its variables * * 'lock[1]' will lock config[1] and its variables * * * * Config class cannot synchronize itself, because * * config[1] could change, and should be synchronized * * as well!! * * * ************************************************************/ /** * Activate secondary config * @param conf PRIMARY/SECONDARY configuration */ private static void activateSecondaryConfig( int conf ) { if ( conf != SECONDARY_CONFIG ) return; synchronized ( lock[SECONDARY_CONFIG] ) { if ( config[PRIMARY_CONFIG] != config[SECONDARY_CONFIG] ) return; } // clone is needed only here Config secondaryConfig = new Config(); synchronized ( lock[PRIMARY_CONFIG] ) { secondaryConfig.enabled = config[PRIMARY_CONFIG].enabled; secondaryConfig.debugEnabled = config[PRIMARY_CONFIG].debugEnabled; secondaryConfig.logTag = config[PRIMARY_CONFIG].logTag; secondaryConfig.fileName = config[PRIMARY_CONFIG].fileName; secondaryConfig.directoryName = config[PRIMARY_CONFIG].directoryName; secondaryConfig.minLimit = config[PRIMARY_CONFIG].minLimit; secondaryConfig.maxLimit = config[PRIMARY_CONFIG].maxLimit; secondaryConfig.timeStampEnabled = config[PRIMARY_CONFIG].timeStampEnabled; secondaryConfig.spaceStampEnabled = config[PRIMARY_CONFIG].spaceStampEnabled; secondaryConfig.context = null; } synchronized ( lock[SECONDARY_CONFIG] ) { // Just to be sure, that it is not changed during this method. if ( config[PRIMARY_CONFIG] == config[SECONDARY_CONFIG] ) config[SECONDARY_CONFIG] = secondaryConfig; // If already changed, we do not modify the previous "secondary config" } } /** * Resets all log config settings to original values. * @param conf PRIMARY/SECONDARY configuration */ private static void resetAll( int conf ) { activateSecondaryConfig( conf ); synchronized ( lock[conf] ) { config[conf].enabled = true; config[conf].debugEnabled = true; config[conf].logTag = DEFAULT_LOG_TAG; config[conf].fileName = defaultFileName; config[conf].directoryName = DEFAULT_DIRECTORY; config[conf].minLimit = 0; config[conf].maxLimit = NO_LIMIT; config[conf].timeStampEnabled = true; config[conf].spaceStampEnabled = false; config[conf].context = null; } } /** * Sets enabled state (main switch) * @param conf PRIMARY/SECONDARY configuration * @param enable true if enabled */ private static void setEnabled( int conf, boolean enable ) { activateSecondaryConfig( conf ); synchronized ( lock[conf] ) { config[conf].enabled = enable; } } /** * Returns enabled state (main switch) * @param conf PRIMARY/SECONDARY configuration * @return true if enabled */ private static boolean isEnabled( int conf ) { synchronized ( lock[conf] ) { return config[conf].enabled; } } /** * Sets enabled state of debug-log * @param conf PRIMARY/SECONDARY configuration * @param enable true if enabled */ private static void setDebugEnabled( int conf, boolean enable ) { activateSecondaryConfig( conf ); synchronized ( lock[conf] ) { config[conf].debugEnabled = enable; } } /** * Returns enabled state of debug-log * @param conf PRIMARY/SECONDARY configuration * @return true if enabled */ private static boolean isDebugEnabled( int conf ) { synchronized ( lock[conf] ) { return config[conf].debugEnabled; } } /** * Sets system-log tag * @param conf PRIMARY/SECONDARY configuration * @param logTag tag appearing in system log, null if disabled */ private static void setSysLog( int conf, String logTag ) { activateSecondaryConfig( conf ); synchronized ( lock[conf] ) { config[conf].logTag = logTag; } } /** * Gets system-log tag * @param conf PRIMARY/SECONDARY configuration * @return system-log tag, null if disabled */ private static String getSysLog( int conf ) { synchronized ( lock[conf] ) { return config[conf].logTag; } } /** * Creates default file name from package name. It will be used as default. * @param context application's context */ private static void setDefaultFileName( Context context ) { defaultFileName = context.getPackageName() + DEFAULT_FILE_EXT; } /** * Sets file name for file-log * @param conf PRIMARY/SECONDARY configuration * @param fileName name of log file, null if disabled */ private static void setFileName( int conf, String fileName ) { activateSecondaryConfig( conf ); synchronized ( lock[conf] ) { config[conf].fileName = fileName; } } /** * Sets directory path (on sd-card) for file-log * @param conf PRIMARY/SECONDARY configuration * @param directoryName directory path; null and "" mean root on sdcard */ private static void setDirectoryName( int conf, String directoryName ) { activateSecondaryConfig( conf ); if ( directoryName == null ) directoryName = ""; synchronized ( lock[conf] ) { config[conf].directoryName = directoryName; } } /** * Gets log-directory (directory name completed with external storage path). * If path does not exist or is not a directory, root of external storage path will be used. * @param conf PRIMARY/SECONDARY configuration * @return log-directory */ private static File getLogDirectory( int conf ) { File directory; synchronized ( lock[conf] ) { directory = new File( Environment.getExternalStorageDirectory(), config[conf].directoryName ); } if ( !directory.isDirectory() ) directory = Environment.getExternalStorageDirectory(); return directory; } /** * Gets log-file (file name completed with directory and external storage path). * If path does not exist or is not a directory, root of external storage path will be used. * @param conf PRIMARY/SECONDARY configuration * @return log-file */ private static File getLogFile( int conf ) { synchronized ( lock[conf] ) { if (config[conf].fileName == null) return null; else return new File( getLogDirectory(conf), config[conf].fileName); } } /** * Gets log-file with version (file name completed with directory and external storage path AND version). * If path does not exist or is not a directory, root of external storage path will be used. * @param conf PRIMARY/SECONDARY configuration * @param version will be append at the end of the basename (0 omitted) * @return log-file */ private static File getLogFile( int conf, int version ) { synchronized ( lock[conf] ) { if (config[conf].fileName == null) return null; else { String base = config[conf].fileName; String ext = ""; int dot = base.lastIndexOf('.'); if ( dot >= 0) { ext = base.substring( dot ); base = base.substring(0, dot); } return new File( getLogDirectory(conf), base + ( version > 0 ? "_" + version : "") + ext); } } } /** * Sets context for toast-log * @param conf PRIMARY/SECONDARY configuration * @param context context of the activity, null if disabled */ private static void setContext( int conf, Context context ) { activateSecondaryConfig( conf ); synchronized ( lock[conf] ) { config[conf].context = context; } } /** * Gets context for toast-log * @param conf PRIMARY/SECONDARY configuration * @return context of the activity, null if disabled */ private static Context getContext( int conf ) { synchronized ( lock[conf] ) { return config[conf].context; } } /** * Sets limits for log-level * @param conf PRIMARY/SECONDARY configuration * @param min lower limit * @param max upper limit ( No restriction above NO_LIMIT!) */ private static void setLimits( int conf, int min, int max ) { activateSecondaryConfig( conf ); synchronized ( lock[conf] ) { config[conf].minLimit = min; config[conf].maxLimit = max; } } /** * Gets lower limit for log-level * @param conf PRIMARY/SECONDARY configuration * @return min limit */ private static int getMinLimit( int conf ) { synchronized ( lock[conf] ) { return config[conf].minLimit; } } /** * Gets upper limit for log-level * @param conf PRIMARY/SECONDARY configuration * @return max limit */ private static int getMaxLimit( int conf ) { synchronized ( lock[conf] ) { return config[conf].maxLimit; } } /** * Check wether level is enabled * @param level messsage level * @return true if level is enabled (within limits or above NO_LIMIT) */ private static boolean isLevelEnabled( int level ) { if ( level>= NO_LIMIT ) return true; int conf = level % 2; if ( level < getMinLimit(conf) || level > getMaxLimit(conf) ) return false; return true; } /** * Sets time stamp enabled for file-log messages * @param conf PRIMARY/SECONDARY configuration * @param enable true if enabled */ private static void setTimeStampEnabled( int conf, boolean enable ) { activateSecondaryConfig( conf ); synchronized ( lock[conf] ) { config[conf].timeStampEnabled = enable; } } /** * Returns time stamp enabled state (for file-log messages) * @param conf PRIMARY/SECONDARY configuration * @return true if enabled */ private static boolean isTimeStampEnabled( int conf ) { synchronized ( lock[conf] ) { return config[conf].timeStampEnabled; } } /** * Current time in format "(yy-MM-dd HH:mm:ss.SSS) ". * @return current time as formatted string (empty string if disabled) */ private static String timeStamp() { SimpleDateFormat sdf=new SimpleDateFormat( "yy-MM-dd HH:mm:ss.SSS", Locale.US ); return "(" + sdf.format( new Date() ) + ") "; } /** * Current time in format "(yy-MM-dd HH:mm:ss.SSS) ", if enabled in this config. * @param conf PRIMARY/SECONDARY configuration * @return current time as formatted string (empty string if disabled) */ private static String timeStamp( int conf ) { if ( !isTimeStampEnabled( conf ) ) return ""; return timeStamp(); } /** * Sets space stamp (class.method) enabled for file-log messages * @param conf PRIMARY/SECONDARY configuration * @param enable true if enabled */ private static void setSpaceStampEnabled( int conf, boolean enable ) { activateSecondaryConfig( conf ); synchronized ( lock[conf] ) { config[conf].spaceStampEnabled = enable; } } /** * Returns space stamp enabled state (for file-log messages) * @param conf PRIMARY/SECONDARY configuration * @return true if enabled */ private static boolean isSpaceStampEnabled( int conf ) { synchronized ( lock[conf] ) { return config[conf].spaceStampEnabled; } } /** * Current position in (class.method) format, if enabled in this config. * @param conf PRIMARY/SECONDARY configuration * @return current position (empty string if disabled) */ private static String spaceStamp( int conf ) { if ( !isSpaceStampEnabled( conf ) ) return ""; StackTraceElement[] element = new Throwable().getStackTrace(); int index = 1; while ( element[index].getFileName().startsWith("Scribe.") ) index++; String c = element[index].getClassName(); int p = c.lastIndexOf('.'); if ( p >= 0) c = c.substring( p+1, c.length()); return " (" + c + "." + element[index].getMethodName() + ")"; } /** * Current position in 'package.class.method (thread)' format * @return current position */ private static String spaceStamp() { StackTraceElement[] element = new Throwable().getStackTrace(); int index = 1; while ( element[index].getFileName().startsWith("Scribe.") ) index++; return element[index].getClassName() + "." + element[index].getMethodName() + " (" + Thread.currentThread().getName() + ")"; } /** * Returns to previously saved config settings. Toast-log will be disabled. * @param conf PRIMARY/SECONDARY configuration * @param state settings returned by {@link #getAll(int)} */ private static void setAll( int conf, Bundle state ) { activateSecondaryConfig( conf ); synchronized ( lock[conf] ) { config[conf].enabled = state.getBoolean("ENABLED", true); config[conf].debugEnabled = state.getBoolean("DEBUG_ENABLED", true); config[conf].logTag = state.getString("LOG_TAG"); if ( config[conf].logTag == null ) config[conf].logTag = DEFAULT_LOG_TAG; config[conf].fileName = state.getString("FILE_NAME"); if ( config[conf].fileName == null ) config[conf].fileName = defaultFileName; config[conf].directoryName = state.getString("DIRECTORY_NAME"); if ( config[conf].directoryName == null ) config[conf].directoryName = DEFAULT_DIRECTORY; config[conf].minLimit = state.getInt("MIN_LIMIT", 0); config[conf].maxLimit = state.getInt("MAX_LIMIT", NO_LIMIT); config[conf].timeStampEnabled = state.getBoolean("TIME_STAMP_ENABLED", true); config[conf].spaceStampEnabled = state.getBoolean("SPACE_STAMP_ENABLED", true); config[conf].context = null; // Ezt veszlyes hosszan hasznlni, tltsnl is kikapcsoljuk! } } /** * Gets all log config settings. Toast-log will be disabled. * @param conf PRIMARY/SECONDARY configuration * @return bundle of primary log config settings */ private static Bundle getAll( int conf ) { Bundle state = new Bundle( 9 ); synchronized ( lock[conf] ) { state.putBoolean("ENABLED", config[conf].enabled); state.putBoolean("DEBUG_ENABLED", config[conf].debugEnabled); state.putString("LOG_TAG", config[conf].logTag); state.putString("FILE_NAME", config[conf].fileName); state.putString("DIRECTORY_NAME", config[conf].directoryName); state.putInt("MIN_LIMIT", config[conf].minLimit); state.putInt("MAX_LIMIT", config[conf].maxLimit); state.putBoolean("TIME_STAMP_ENABLED", config[conf].timeStampEnabled); state.putBoolean("SPACE_STAMP_ENABLED", config[conf].spaceStampEnabled); config[conf].context = null; // Ezt veszlyes hosszan hasznlni, mentsnl is kikapcsoljuk! } return state; } /************************************************************ * * * Intializations * * * * Initialization is not needed. * * Configs are stored on application level (static), * * it is a good idea to reset configs at entry level. * * (Application, Activity etc.) * * * ************************************************************/ /** * Configs reset to initial values, secondary config is inactivated. * Default file name resets to DEFAULT_FILE_NAME. */ public static void init() { synchronized ( lock[SECONDARY_CONFIG] ) { config[SECONDARY_CONFIG] = primaryConfig; } defaultFileName = DEFAULT_FILE_NAME; // defaultFileName is volatile !! resetAll( PRIMARY_CONFIG ); } /** * Configs reset to initial values, secondary config is inactivated. * Default file name will be the package name. */ public static void init( Context context ) { synchronized ( lock[SECONDARY_CONFIG] ) { config[SECONDARY_CONFIG] = primaryConfig; } setDefaultFileName( context ); // defaultFileName is volatile !! resetAll( PRIMARY_CONFIG ); } /************************************************************ * * * User control of PRIMARY config * * * * Primary config is used for ALL message levels * * before activation of a secondary config * * Primary config is used only for EVEN message leves * * after activation of secondary config * * * ************************************************************/ /** * Returns log config. Toast-log will be disabled. * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config * @return bundle of log config settings * <p> * @see #setConfig(Bundle) * @see #getSecondaryConfig() */ public static Bundle getConfig() { return getAll( PRIMARY_CONFIG ); } /** * Returns to previously saved config settings. Toast-log will be disabled. * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config * @param state settings returned by {@link #getState()} * <p> * @see #getConfig() * @see #setSecondaryConfig() */ public static void setConfig( Bundle state ) { setAll( PRIMARY_CONFIG, state ); } /** * Enables logging (main "switch") * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config */ public static void enable() { setEnabled( PRIMARY_CONFIG, true ); } /** * Disables logging (main "switch") * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config */ public static void disable() { setEnabled( PRIMARY_CONFIG, false ); } /** * Enables debug-logging * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config */ public static void enableDebug() { setDebugEnabled( PRIMARY_CONFIG, true ); } /** * Disables debug-logging * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config */ public static void disableDebug() { setDebugEnabled( PRIMARY_CONFIG, false ); } /** * Enables logging to system-log * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config * @param logTag tag appearing in system log */ public static void enableSysLog( String logTag ) { setSysLog( PRIMARY_CONFIG, logTag ); } /** * Enables logging to system-log with default tag * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config */ public static void enableSysLog() { setSysLog( PRIMARY_CONFIG, DEFAULT_LOG_TAG ); } /** * Disables logging to system-log * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config */ public static void disableSysLog() { setSysLog( PRIMARY_CONFIG, null ); } /** * Enables logging to file log under default file name * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config * @param fileName log-file's name */ public static void enableFileLog( String fileName ) { setFileName( PRIMARY_CONFIG, fileName ); } /** * Enables logging to file log under default file name * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config */ public static void enableFileLog() { setFileName( PRIMARY_CONFIG, defaultFileName ); } /** * Disables logging to file log * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config */ public static void disableFileLog() { setFileName( PRIMARY_CONFIG, null ); } /** * Enables logging to toast log * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config * @param context context of the activity */ public static void enableToastLog( Context context ) { setContext( PRIMARY_CONFIG, context ); } /** * Disables logging to toast log * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config */ public static void disableToastLog() { setContext( PRIMARY_CONFIG, null ); } /** * Sets directory path (on sd-card) for log-file * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config * @param directoryName path of logging directory */ public static void setDirectoryName( String directoryName ) { setDirectoryName( PRIMARY_CONFIG, directoryName ); } /** * Sets lower and upper limit. Messages outside these limits will be disabled. * (No restriction for limits above NO_LIMIT!) * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config * @param maxLimit */ public static void setLimits( int min, int max ) { setLimits( PRIMARY_CONFIG, min, max); } /** * Sets lower limit and clears upper limit. * Messages below lower limit will be disabled. * @param minLimit lower limit */ public static void setMinLimit( int minLimit ) { setLimits( PRIMARY_CONFIG, minLimit, NO_LIMIT ); } /** * Sets upper limit and clears lower limit. Messages above upper limit will be disabled. * (No restriction for limits above NO_LIMIT!) * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config * @param maxLimit upper limit */ public static void setMaxLimit( int maxLimit ) { setLimits( PRIMARY_CONFIG, 0, maxLimit ); } /** * Resets lower and upper limit to their original values. No messages will be limited. * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config */ public static void resetLimits( ) { setLimits( PRIMARY_CONFIG, 0, NO_LIMIT ); } /** * Enables time stamp in file-log * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config */ public static void enableTimeStamp() { setTimeStampEnabled( PRIMARY_CONFIG, true ); } /** * Disables time stamp in file-log * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config */ public static void disableTimeStamp() { setTimeStampEnabled( PRIMARY_CONFIG, false ); } /** * Enables space stamp (class.method) in file-log * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config */ public static void enableSpaceStamp() { setSpaceStampEnabled( PRIMARY_CONFIG, true ); } /** * Disables space stamp (class.method) in file-log * PRIMARY Config - valid for ALL/EVEN levels after activation of sec. config */ public static void disableSpaceStamp() { setSpaceStampEnabled( PRIMARY_CONFIG, false ); } /************************************************************ * * * User control of Secondary config * * * * Secondary config is used for ODD message levels * * Secondary config will be active only * * after the first modification of sec. config values * * * ************************************************************/ /** * Returns log config. Toast-log will be disabled. * SECONDARY Config - valid for ALL/ODD levels after activation of sec. config * @return bundle of log config settings * <p> * @see #getConfig(Bundle) * @see #setConfigSecondary() */ public static Bundle getConfigSecondary() { return getAll( SECONDARY_CONFIG ); } /** * Returns to previously saved config settings. Toast-log will be disabled. * SECONDARY Config - valid for ODD levels. Secondary config will be activated * @param state settings returned by {@link #getConfigSecondary()} * <p> * @see #setConfig() * @see #getConfigSecondary() */ public static void setConfigSecondary( Bundle state ) { setAll( SECONDARY_CONFIG, state ); } /** * Enables logging (main "switch") * SECONDARY Config - valid for ODD levels. Secondary config will be activated */ public static void enableSecondary() { setEnabled( SECONDARY_CONFIG, true ); } /** * Disables logging (main "switch") * SECONDARY Config - valid for ODD levels. Secondary config will be activated */ public static void disableSecondary() { setEnabled( SECONDARY_CONFIG, false ); } /** * Enables debug-logging * SECONDARY Config - valid for ODD levels. Secondary config will be activated */ public static void enableDebugSecondary() { setDebugEnabled( SECONDARY_CONFIG, true ); } /** * Disables debug-logging * SECONDARY Config - valid for ODD levels. Secondary config will be activated */ public static void disableDebugSecondary() { setDebugEnabled( SECONDARY_CONFIG, false ); } /** * Enables logging to system-log * SECONDARY Config - valid for ODD levels. Secondary config will be activated * @param logTag tag appearing in system log */ public static void enableSysLogSecondary( String logTag ) { setSysLog( SECONDARY_CONFIG, logTag ); } /** * Enables logging to system-log with default tag * SECONDARY Config - valid for ODD levels. Secondary config will be activated */ public static void enableSysLogSecondary() { setSysLog( SECONDARY_CONFIG, DEFAULT_LOG_TAG ); } /** * Disables logging to system-log * SECONDARY Config - valid for ODD levels. Secondary config will be activated */ public static void disableSysLogSecondary() { setSysLog( SECONDARY_CONFIG, null ); } /** * Enables logging to file log under default file name * SECONDARY Config - valid for ODD levels. Secondary config will be activated * @param fileName log-file's name */ public static void enableFileLogSecondary( String fileName ) { setFileName( SECONDARY_CONFIG, fileName ); } /** * Enables logging to file log under default file name * SECONDARY Config - valid for ODD levels. Secondary config will be activated */ public static void enableFileLogSecondary() { setFileName( SECONDARY_CONFIG, defaultFileName ); } /** * Disables logging to file log * SECONDARY Config - valid for ODD levels. Secondary config will be activated */ public static void disableFileLogSecondary() { setFileName( SECONDARY_CONFIG, null ); } /** * Enables logging to toast log * SECONDARY Config - valid for ODD levels. Secondary config will be activated * @param context context of the activity */ public static void enableToastLogSecondary( Context context ) { setContext( SECONDARY_CONFIG, context ); } /** * Disables logging to toast log * SECONDARY Config - valid for ODD levels. Secondary config will be activated */ public static void disableToastLogSecondary() { setContext( SECONDARY_CONFIG, null ); } /** * Sets directory path (on sd-card) for log-file * SECONDARY Config - valid for ODD levels. Secondary config will be activated * @param directoryName path of logging directory */ public static void setDirectoryNameSecondary( String directoryName ) { setDirectoryName( SECONDARY_CONFIG, directoryName ); } /** * Sets lower and upper limit. Messages outside these limits will be disabled. * (No restriction for limits above NO_LIMIT!) * SECONDARY Config - valid for ODD levels. Secondary config will be activated * @param maxLimit */ public static void setLimitsSecondary( int min, int max ) { setLimits( SECONDARY_CONFIG, min, max); } /** * Sets lower limit and clears upper limit. Messages below lower limit will be disabled. * SECONDARY Config - valid for ODD levels. Secondary config will be activated * @param minLimit lower limit */ public static void setMinLimitSecondary( int minLimit ) { setLimits( SECONDARY_CONFIG, minLimit, NO_LIMIT ); } /** * Sets upper limit and clears lower limit. Messages above upper limit will be disabled. * (No restriction for limits above NO_LIMIT!) * SECONDARY Config - valid for ODD levels. Secondary config will be activated * @param maxLimit upper limit */ public static void setMaxLimitSecondary( int maxLimit ) { setLimits( SECONDARY_CONFIG, 0, maxLimit ); } /** * Resets lower and upper limit to their original values. No messages will be limited. * SECONDARY Config - valid for ODD levels. Secondary config will be activated */ public static void resetLimitsSecondary( ) { setLimits( SECONDARY_CONFIG, 0, NO_LIMIT ); } /** * Enables time stamp in file-log * SECONDARY Config - valid for ODD levels. Secondary config will be activated */ public static void enableTimeStampSecondary() { setTimeStampEnabled( SECONDARY_CONFIG, true ); } /** * Disables time stamp in file-log * SECONDARY Config - valid for ODD levels. Secondary config will be activated */ public static void disableTimeStampSecondary() { setTimeStampEnabled( SECONDARY_CONFIG, false ); } /** * Enables space stamp (class.method) in file-log * SECONDARY Config - valid for ODD levels. Secondary config will be activated */ public static void enableSpaceStampSecondary() { setSpaceStampEnabled( SECONDARY_CONFIG, true ); } /** * Disables space stamp (class.method) in file-log * SECONDARY Config - valid for ODD levels. Secondary config will be activated */ public static void disableSpaceStampSecondary() { setSpaceStampEnabled( SECONDARY_CONFIG, false ); } /************************************************************ * * * Private methods for log messaging * * * ************************************************************/ /** * Different types of log * <ul> * <li>TITLE Title log style</li> * <li>NOTE Normal log style</li> * <li>DEBUG Normal log style, appears only if debug-log is enabled</li> * <li>ERROR Error log style</li> * <li>LOCUS Information about method, class, file, thread</li> * </ul> */ private static enum Type { TITLE, NOTE, DEBUG, ERROR, LOCUS }; /** * Adds text with type to system log, toast log and file log. * Log note appears only if log is enabled and only on enabled log streams. * @param type log style * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log */ private static String addText( Type type, int level, String text ) { int conf = level % 2; if ( !isEnabled(conf) ) return OFF; if ( (type == Type.DEBUG || type == Type.LOCUS) && !isDebugEnabled(conf) ) return OFF; if ( !isLevelEnabled(level) ) return OFF; addTextToSysLog( type, conf, text ); addTextToToastLog( type, conf, text ); return addTextToFileLog( type, conf, text ); } /** * Adds text with type to toast log. * Can be sent from any thread, toast will displayed from main thread * Log note appears only if logging to toast log is enabled. * (Main enable is not checked) * @param type log style * @param text log note */ private static void addTextToToastLog( Type type, final int conf, final String text ) { final Context context = getContext(conf); // Log a kepernyore if ( context != null ) { // http://stackoverflow.com/questions/18280012/how-to-replace-the-system-out-with-toasts-inside-a-thread/18280318#18280318 /* * activity.runOnUiThread(new Runnable() * { * public void run() * { * Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show(); * } * }); */ new Handler( Looper.getMainLooper() ).post( new Runnable() { @Override public void run() { Toast.makeText( context, text, Toast.LENGTH_SHORT).show(); } }); // Toast.makeText( context[0], text, Toast.LENGTH_SHORT ).show(); } } /** * Adds text with type to system log. * Log note appears only if logging to system log is enabled. * (Main enable is not checked) * @param type log style * @param text log note */ private static void addTextToSysLog( Type type, int conf, String text ) { String logTag = getSysLog(conf); // Log a syslog-ba if ( logTag != null ) { switch (type) { case TITLE: Log.i( logTag, " *** " + text + " ***" ); break; case NOTE: Log.i( logTag, text ); break; case DEBUG: Log.d( logTag, text ); break; case ERROR: Log.e( logTag, text ); break; case LOCUS: Log.d( logTag, text ); break; default: } } } /** * Adds text with type to file log. * Log note appears only if logging to file log is enabled. * (Main enable is not checked) * @param type log style * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) */ private static String addTextToFileLog( Type type, int conf, String text ) { File logFile = getLogFile( conf ); // Log a file-ba if ( logFile != null ) { OutputStreamWriter logStream = null; try { logStream = new OutputStreamWriter( new FileOutputStream(logFile, true) ); switch (type) { case TITLE: logStream.append( SEPARATOR + " *** " + text + " ***" + SEPARATOR + SEPARATOR ); break; case NOTE: case DEBUG: logStream.append( timeStamp(conf) + text + spaceStamp(conf) + SEPARATOR); break; case ERROR: logStream.append( timeStamp(conf) + "ERROR: " + text + spaceStamp(conf) + SEPARATOR ); break; case LOCUS: logStream.append( timeStamp(conf) + text + SEPARATOR ); break; } logStream.flush(); } catch (IOException ioe) { // A hibat a visszateresi ertek mutatja, DE // a bekapcsolas fuggvenyeben a tobbi log is kiadhatja String err = LOGFILE_ERROR + ioe.toString(); addTextToToastLog( Type.ERROR, conf, err ); addTextToSysLog( Type.ERROR, conf, err ); // visszateresben mindig adja return err; } finally { if (logStream != null) { try { logStream.close(); } catch (IOException ioe) { // Ezt a hibt vgkpp nem tudjuk hol jelenteni... } } } } return OK; } /************************************************************ * * * Public log messaging methods * * * ************************************************************/ /** * Adds text as type {@code TITLE} to system log, toast log and file log. * Log note appears only on enabled log streams and only if limit allows it. * Primary config is used for EVEN, secondary for ODD levels * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log */ public static String title(int level, String text) { return addText( Type.TITLE, level, text ); } /** * Adds text as type {@code NOTE} to system log, toast log and file log. * Log note appears only on enabled log streams, and only if limit allows it. * Primary config is used for EVEN, secondary for ODD levels * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log */ public static String note(int level, String text) { return addText( Type.NOTE, level, text ); } /** * Adds text as type {@code DEBUG} to system log, toast log and file log. * Log note appears only if debug-log is enabled and only on enabled log streams, * and only if limit allows it. * Primary config is used for EVEN, secondary for ODD levels * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log */ public static String debug(int level, String text) { return addText( Type.DEBUG, level, text ); } /** * Adds text as type {@code ERROR} to system log, toast log and file log. * Log note appears only on enabled log streams, and only if limit allows it. * Primary config is used for EVEN, secondary for ODD levels * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log */ public static String error(int level, String text) { return addText( Type.ERROR, level, text ); } /** * Adds text as type {@code METHOD} to system log, toast log and file log. * Log note appears only on enabled log streams, and only if limit allows it. * Primary config is used for EVEN, secondary for ODD levels * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log */ public static String locus( int level ) { return addText( Type.LOCUS, level, "@ " + spaceStamp() ); } // Public methods without limit check /** * Adds text as type {@code TITLE} to system log, toast log and file log. * Log note appears only on enabled log streams. No limit check. * Primary config is used. * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log * @see #checkLogFileLength() */ public static String title(String text) { return title( NO_LIMIT, text); } /** * Adds text as type {@code NOTE} to system log, toast log and file log. * Log note appears only on enabled log streams. No limit check. * Primary config is used. * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log */ public static String note(String text) { return note( NO_LIMIT, text); } /** * Adds text as type {@code DEBUG} to system log, toast log and file log. * Log note appears only if debug-log is enabled and only on enabled log streams. * No limit check. * Primary config is used. * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log */ public static String debug(String text) { return debug( NO_LIMIT, text); } /** * Adds text as type {@code ERROR} to system log, toast log and file log. * Log note appears only on enabled log streams. No limit check. * Primary config is used. * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log */ public static String error(String text) { return error( NO_LIMIT, text); } /** * Adds text as type {@code METHOD} to system log, toast log and file log. * Log note appears only on enabled log streams. No limit check. * Primary config is used. * @return message ({@link #OK}, {@link #OFF} or error) from file-log */ public static String locus() { return locus( NO_LIMIT ); } // Public methods for Secondary config without limit check /** * Adds text as type {@code TITLE} to system log, toast log and file log. * Log note appears only on enabled log streams. No limit check. * Secondary config is used. * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log * @see #checkLogFileLength() */ public static String title_secondary(String text) { return title( NO_LIMIT_SECONDARY, text); } /** * Adds text as type {@code NOTE} to system log, toast log and file log. * Log note appears only on enabled log streams. No limit check. * Secondary config is used. * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log */ public static String note_secondary(String text) { return note( NO_LIMIT_SECONDARY, text); } /** * Adds text as type {@code DEBUG} to system log, toast log and file log. * Log note appears only if debug-log is enabled and only on enabled log streams. * No limit check. * Secondary config is used. * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log */ public static String debug_secondary(String text) { return debug( NO_LIMIT_SECONDARY, text); } /** * Adds text as type {@code ERROR} to system log, toast log and file log. * Log note appears only on enabled log streams. No limit check. * Secondary config is used. * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log */ public static String error_secondary(String text) { return error( NO_LIMIT_SECONDARY, text); } /** * Adds text as type {@code METHOD} to system log, toast log and file log. * Log note appears only on enabled log streams. No limit check. * Secondary config is used. * @param text log note * @return message ({@link #OK}, {@link #OFF} or error) from file-log */ public static String locus_secondary(String text) { return locus( NO_LIMIT_SECONDARY ); } /************************************************************ * * * Private methods for log file operations * * * ************************************************************/ /** * Deletes log file. * Works only if logging and file logging are enabled. * Result is logged on system log and toast log (logs - even file log! - should be enabled!) * @return result: ({@link #OK}, {@link #OFF} or error) */ private static String clear( int conf ) { conf = conf % 2; // Just for security - public methods if ( !isEnabled(conf) ) return OFF; File logFile = getLogFile( conf ); String err; if ( logFile == null ) return OFF; if ( logFile.delete() ) { err = "<" + logFile.getName() + "> cleared."; // sikeres torlest a tobbi logon is jelezzuk addTextToToastLog( Type.NOTE, conf, err ); addTextToSysLog( Type.NOTE, conf, err ); return OK; } err = "Cannot clear <" + logFile.getName() + ">!"; // Sikertelenseg eseten is jelzunk addTextToToastLog( Type.ERROR, conf, err ); addTextToSysLog( Type.ERROR, conf, err ); return LOGFILE_ERROR + err; } /** * Checks the length of the log file. * Works only if logging and file logging are enabled. * If file length is longer than {@link MAX_LOGFILE_LENGTH} then * old logfile is archived, and a new one will be created. * The maximum number of archived log files is definied in {@link MAX_LOGFILE}. * The oldest log file will be deleted after reaching this limit. * Result is logged on system log and toast log (logs - even file log! - should be enabled!) * @return result: ({@link #OK}, {@link #OFF} or error) * @see #title(String) */ private static String checkLogFileLength( int conf ) { if ( !isEnabled(conf) ) return OFF; File logFile = getLogFile(conf); String err; if ( logFile == null ) return OFF; boolean ok = true; FileChannel logChannel = null; FileLock lock = null; try { logChannel = new RandomAccessFile( logFile, "rw" ).getChannel(); lock = logChannel.lock(); if ( logFile.length() > MAX_LOGFILE_LENGTH ) { File nextFile; File prevFile; for (int cnt = MAX_LOGFILE; cnt > 0; cnt--) { nextFile = getLogFile( conf, cnt ); prevFile = getLogFile( conf, cnt-1 ); if ( nextFile.exists() ) ok &= nextFile.delete(); if ( prevFile.exists() ) ok &= prevFile.renameTo( nextFile ); } } else { return OK; // No changes needed } } catch (IOException e) { ok = false; // Exception could occur only in lock! } finally { if ( lock != null ) { try { lock.release(); } catch (IOException e) { // Ezt a hibt vgkpp nem tudjuk hol jelenteni... } } if ( logChannel != null) { try { logChannel.close(); } catch (IOException e) { // Ezt a hibt vgkpp nem tudjuk hol jelenteni... } } } if ( ok ) { err = "((Log file length check: new <" + logFile.getName() + "> was created.))"; // sikert a tobbi logon is jelezzuk addTextToToastLog( Type.NOTE, conf, err ); addTextToSysLog( Type.NOTE, conf, err ); return OK; } err = "((Log file length check: unable to create new <" + logFile.getName() + ">!))"; // Sikertelenseg eseten is jelzunk addTextToToastLog( Type.ERROR, conf, err ); addTextToSysLog( Type.ERROR, conf, err ); return LOGFILE_ERROR + err; } /************************************************************ * * * Public methods for log file operations * * * ************************************************************/ /** * Deletes log file - PRIMARY CONFIG. * Works only if logging and file logging are enabled. * Result is logged on system log and toast log (logs - even file log! - should be enabled!) * @return result: ({@link #OK}, {@link #OFF} or error) */ public static String clear() { return clear( PRIMARY_CONFIG ); } /** * Checks the length of the log file - PRIMARY CONFIG. * Works only if logging and file logging are enabled. * If file length is longer than {@link MAX_LOGFILE_LENGTH} then * old logfile is archived, and a new one will be created. * The maximum number of archived log files is definied in {@link MAX_LOGFILE}. * The oldest log file will be deleted after reaching this limit. * Result is logged on system log and toast log (logs - even file log! - should be enabled!) * @return result: ({@link #OK}, {@link #OFF} or error) * @see #title(String) */ public static String checkLogFileLength() { return checkLogFileLength( PRIMARY_CONFIG ); } /** * Deletes log file - SECONDARY CONFIG. * Works only if logging and file logging are enabled. * Result is logged on system log and toast log (logs - even file log! - should be enabled!) * @return result: ({@link #OK}, {@link #OFF} or error) */ public static String clear_secondary() { return clear( SECONDARY_CONFIG ); } /** * Checks the length of the log file - SECONDARY CONFIG. * Works only if logging and file logging are enabled. * If file length is longer than {@link MAX_LOGFILE_LENGTH} then * old logfile is archived, and a new one will be created. * The maximum number of archived log files is definied in {@link MAX_LOGFILE}. * The oldest log file will be deleted after reaching this limit. * Result is logged on system log and toast log (logs - even file log! - should be enabled!) * @return result: ({@link #OK}, {@link #OFF} or error) * @see #title(String) */ public static String checkLogFileLength_secondary() { return checkLogFileLength( SECONDARY_CONFIG ); } /************************************************************ * * * System log controls - works only with PRIMARY config * * * ************************************************************/ /** * Dumps full system log to file log. * Works only if logging and file logging are enabled. * Result is logged on system log and toast log (logs - even file log! - should be enabled!) * @return result: ({@link #OK}, {@link #OFF} or error) */ public static String dumpSysLog() { int conf = PRIMARY_CONFIG; if ( !isEnabled(conf) ) return OFF; File logFile = getLogFile(conf); if ( logFile == null ) return OFF; OutputStreamWriter logStream = null; BufferedReader bufferedReader = null; Process process = null; String err; try { process = Runtime.getRuntime().exec("logcat -d -v time"); bufferedReader = new BufferedReader( new InputStreamReader( process.getInputStream() )); logStream = new OutputStreamWriter( new FileOutputStream(logFile, true) ); String line; logStream.append( SEPARATOR + "--- SYSTEM LOG DUMP at " + timeStamp(conf) + "---" + SEPARATOR + SEPARATOR); while ( (line = bufferedReader.readLine()) != null ) { logStream.append(line + SEPARATOR); } logStream.append(SEPARATOR + "--- END OF SYSTEM LOG ---" + SEPARATOR + SEPARATOR); logStream.flush(); err = "<" + logFile.getName() + "> system log dump ready"; // Sikeres befejezes addTextToToastLog( Type.NOTE, conf, err ); addTextToSysLog( Type.NOTE, conf, err); return OK; } catch (IOException ioe) { err = LOGFILE_ERROR + ioe.toString(); // Sikertelenseg addTextToToastLog( Type.ERROR, conf, err ); addTextToSysLog( Type.ERROR, conf, err ); return err; } finally { if (logStream != null) { try { logStream.close(); } catch (IOException ioe) { // Ezt a hibt vgkpp nem tudjuk hol jelenteni... } } if (bufferedReader != null) { try { bufferedReader.close(); } catch (IOException ioe) { // Ezt a hibt vgkpp nem tudjuk hol jelenteni... } } if (process != null) { process.destroy(); } } } /** * Clears system log. * Works only if logging is enabled. * Result is logged on file log and toast log (logs should be enabled!) * @return result: ({@link #OK}, {@link #OFF} or error) */ public static String clearSysLog( ) { int conf = PRIMARY_CONFIG; if ( !isEnabled(conf) ) return OFF; Process process = null; String err; try { process = Runtime.getRuntime().exec("logcat -c"); err = "System log cleared."; // Siker addTextToToastLog( Type.NOTE, conf, err ); addTextToFileLog( Type.NOTE, conf, err ); return OK; } catch (IOException ioe) { err = "SYSLOG ERROR: " + ioe.toString(); // sikertelen addTextToToastLog( Type.ERROR, conf, err ); addTextToFileLog( Type.ERROR, conf, err ); return err; } finally { if (process != null) { process.destroy(); } } } /************************************************************ * * * Uncaught exceptions * * * ************************************************************/ /** System's default exception handler */ private static UncaughtExceptionHandler defaultUncaughtExceptionHandler = null; /** * Uncaught exceptions will be catched. * If enabled, exceptions will be logged on PRIMARY file-log. * After logging, exceptions are given to the default system handler, * which dumps it to the system log, and finishes our program * <p> * Uncaught exceptions log is not locked. Synchronization could be performed on an outer lock, * to prevent mixed logs. But two exceptions from different threads at the same time can be a rarity. * Log-lines are mixed with other file-logs, but this is a feature, not a bug! * (And could be avoided by synchronization of each file-log writes.) */ public static void logUncaughtExceptions() { if ( defaultUncaughtExceptionHandler != null ) return; // already set! defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler( new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread thread, Throwable ex) { int conf = PRIMARY_CONFIG; logUncaughtException( conf, thread, ex ); // re-throw critical exception further to the os (important) defaultUncaughtExceptionHandler.uncaughtException(thread, ex); } }); } /** * Eventually log Thread and Throwable to file log. * @param conf PRIMARY/SECONDARY config. Now only primary is used. * @param thread thread where the exception occured * @param ex exception */ private static void logUncaughtException( int conf, Thread thread, Throwable ex) { if ( !isEnabled(conf) ) return; File logFile = getLogFile(conf); if ( logFile == null ) return; OutputStreamWriter logStream = null; try { logStream = new OutputStreamWriter( new FileOutputStream(logFile, true) ); logStream.append( SEPARATOR + "*** Uncaught exception: " + thread.getName() + " " + timeStamp(conf) + " ***" + SEPARATOR + SEPARATOR); logExceptionHistory( logStream, ex ); logStream.append( SEPARATOR + "*** End of uncaught exception ***" + SEPARATOR + SEPARATOR); logStream.flush(); } catch (IOException ioe) { // Can not handle these exceptions } finally { if (logStream != null) { try { logStream.close(); } catch (IOException ioe) { // Can not handle these exceptions } } } } /** * Recursively logs exceptions and their stack-traces. * Private method for logUncaughtException. * Exceptions are logged in different order than in system-log! * There are no abbreviations, all stack-traces are printed! * @param logStream where to log * @param ex exception to log * @throws IOException exception is thrown, but at the end this is not reported */ private static void logExceptionHistory(OutputStreamWriter logStream, Throwable ex) throws IOException { if ( ex.getCause() != null ) { logExceptionHistory( logStream, ex.getCause() ); logStream.append( SEPARATOR + "*** Caused:" + SEPARATOR ); } logStream.append( "*** " + ex.getClass().getName() + SEPARATOR + " >" + ex.getMessage() + "<" + SEPARATOR ); for ( StackTraceElement element: ex.getStackTrace() ) { logStream.append( " @ " + element.getClassName() + "." + element.getMethodName() + " (" + element.getFileName() + ( element.getLineNumber()<0 ? "" : "/" + element.getLineNumber() ) + ")" + SEPARATOR ); } } }