MCPcopy Index your code
hub / github.com/elvishew/xLog

github.com/elvishew/xLog @1.11.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release 1.11.1 ↗ · + Follow
504 symbols 1,212 edges 80 files 288 documented · 57%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

XLog

简体中文

Lightweight and pretty, powerful and flexible logger for android and java, can print the log to Logcat, Console and Files, or anywhere if you like.

Logcat Output

Quick Start

Dependency

implementation 'com.elvishew:xlog:1.11.1'

Initialization

XLog.init(LogLevel.ALL);

Logging

XLog.d("hello xlog");

Logging

Log simple message.

XLog.d(message);

Log a message with throwable, usually use when exception is thrown.

XLog.e(message, throwable);

Format string is also supported, so you don't have to append so many strings and variables by +.

XLog.d("Hello %s, I am %d", "Elvis", 20);

Unformatted JSON and XML string will be formatted automatically.

XLog.json(JSON_CONTENT);
XLog.xml(XML_CONTENT);

All Collections and Maps data are supported.

XLog.d(array);
XLog.d(list);
XLog.d(map);

If needed, you can also dump Intent and Bundle object directly.

XLog.d(intent);
XLog.d(bundle);

In fact, you can dump any type of object if you want. You can specify an ObjectFormatter for specific type, otherwise toString() will be used when converting the object to a string.

XLog.d(object);

Note: v/d/i/w/e are optional, v for VERBOSE, d for DEBUG, i for INFO, w for WARNING and e for ERROR.

Config

xLog is very flexible, almost every component is configurable.

When initialization, there are a simple way

XLog.init(LogLevel.ALL);

and advance way.

LogConfiguration config = new LogConfiguration.Builder()
    .logLevel(BuildConfig.DEBUG ? LogLevel.ALL             // Specify log level, logs below this level won't be printed, default: LogLevel.ALL
        : LogLevel.NONE)
    .tag("MY_TAG")                                         // Specify TAG, default: "X-LOG"
    .enableThreadInfo()                                    // Enable thread info, disabled by default
    .enableStackTrace(2)                                   // Enable stack trace info with depth 2, disabled by default
    .enableBorder()                                        // Enable border, disabled by default
    .jsonFormatter(new MyJsonFormatter())                  // Default: DefaultJsonFormatter
    .xmlFormatter(new MyXmlFormatter())                    // Default: DefaultXmlFormatter
    .throwableFormatter(new MyThrowableFormatter())        // Default: DefaultThrowableFormatter
    .threadFormatter(new MyThreadFormatter())              // Default: DefaultThreadFormatter
    .stackTraceFormatter(new MyStackTraceFormatter())      // Default: DefaultStackTraceFormatter
    .borderFormatter(new MyBoardFormatter())               // Default: DefaultBorderFormatter
    .addObjectFormatter(AnyClass.class,                    // Add formatter for specific class of object
        new AnyClassObjectFormatter())                     // Use Object.toString() by default
    .addInterceptor(new BlacklistTagsFilterInterceptor(    // Add blacklist tags filter
        "blacklist1", "blacklist2", "blacklist3"))
    .addInterceptor(new MyInterceptor())                   // Add other log interceptor
    .build();

Printer androidPrinter = new AndroidPrinter(true);         // Printer that print the log using android.util.Log
Printer consolePrinter = new ConsolePrinter();             // Printer that print the log to console using System.out
Printer filePrinter = new FilePrinter                      // Printer that print(save) the log to file
    .Builder("<path-to-logs-dir>")                         // Specify the directory path of log file(s)
    .fileNameGenerator(new DateFileNameGenerator())        // Default: ChangelessFileNameGenerator("log")
    .backupStrategy(new NeverBackupStrategy())             // Default: FileSizeBackupStrategy(1024 * 1024)
    .cleanStrategy(new FileLastModifiedCleanStrategy(MAX_TIME))     // Default: NeverCleanStrategy()
    .flattener(new MyFlattener())                          // Default: DefaultFlattener
    .writer(new MyWriter())                                // Default: SimpleWriter
    .build();

XLog.init(                                                 // Initialize XLog
    config,                                                // Specify the log configuration, if not specified, will use new LogConfiguration.Builder().build()
    androidPrinter,                                        // Specify printers, if no printer is specified, AndroidPrinter(for Android)/ConsolePrinter(for java) will be used.
    consolePrinter,
    filePrinter);

After initialization, a global Logger with the global config is created, all logging via XLog will pass to this global Logger.

Besides, you can create unlimmited number of Logger with different configs:

  • Base on global Logger, change tag to "TAG-A".
Logger logger = XLog.tag("TAG-A")
                    ... // other overrides
                    .build();
logger.d("Message with custom tag");
  • Base on global Logger, enable border and thread info.
Logger logger = XLog.enableBorder()
                    .enableThread()
                    ... // other overrides
                    .build();
logger.d("Message with thread info and border");

you can also log with one-time-use config:

XLog.tag("TAG-A").d("Message with custom tag");
XLog.enableBorder().enableThread().d("Message with thread info and border");

Print to anywhere

With one logging statement

XLog.d("hello xlog");

you can print the "hello xlog" to

  • Logcat (with AndroidPrinter)

  • File (with FilePrinter)

and anywhere you like.

Just implement the Printer interface, and specify it when initializing

XLog.init(config, printer1, printer2...printerN);

or when creating a non-global Logger

Logger logger = XLog.printer(printer1, printer2...printerN)
                    .build();

or when one-time-use logging

XLog.printer(printer1, printer2...printerN).d("Message with one-time-use printers");

Save logs to file

To save logs to file, you need to create a FilePrinter

Printer filePrinter = new FilePrinter                      // Printer that print(save) the log to file
    .Builder("<path-to-logs-dir>")                         // Specify the directory path of log file(s)
    .fileNameGenerator(new DateFileNameGenerator())        // Default: ChangelessFileNameGenerator("log")
    .backupStrategy(new NeverBackupStrategy())             // Default: FileSizeBackupStrategy(1024 * 1024)
    .cleanStrategy(new FileLastModifiedCleanStrategy(MAX_TIME))     // Default: NeverCleanStrategy()
    .flattener(new MyFlattener())                          // Default: DefaultFlattener
    .build();

and add the FilePrinter to XLog when initializing

XLog.init(config, filePrinter);

or when creating an non-global Logger

Logger logger = XLog.printer(filePrinter)
                    ... // other overrides
                    .build();

or when one-time-use logging

XLog.printer(filePrinter).d("Message with one-time-use printers");

Save third party logs

You can config LibCat after initializing XLog.

LibCat.config(true, filePrinter);

Then, the logs logged by third party modules/libraries(within same app) will be saved to file too.

Go to [LibCat] for more details.

Custom file name

You can specify the file name directly, or categorize the logs to different files by some rules.

  • Use ChangelessFileNameGenerator, you can specify a changeless file name.
logs-dir
└──log
  • Use LevelFileNameGenerator, it will categorize logs by levels automatically.
logs-dir
├──VERBOSE
├──DEBUG
├──INFO
├──WARN
└──ERROR
  • Use DateFileNameGenerator, it will categorize logs by date automatically.
logs-dir
├──2020-01-01
├──2020-01-02
├──2020-01-03
└──2020-01-04
  • Implement FileNameGenerator directly, make the file name generating rules by yourself.
logs-dir
├──2020-01-01-<hash1>.log
├──2020-01-01-<hash2>.log
├──2020-01-03-<hash>.log
└──2020-01-05-<hash>.log

By default, a ChangelessFileNameGenerator with log file name log is used.

Custom log format

Log elements(date, time, level and message) should be flattened to a single string before being printed to the file, you need a Flattener to do that.

We have defined a PatternFlattener, which may satisfy most of you. All you need to do is just passing a pattern with parameters to the flattener.

Supported parameters:

Parameter Represents
{d} Date in default date format "yyyy-MM-dd HH:mm:ss.SSS"
{d format} Date in specific date format
{l} Short name of log level. e.g: V/D/I
{L} Long name of log level. e.g: VERBOSE/DEBUG/INFO
{t} Tag of log
{m} Message of log

Imagine there is a log, with DEBUG level, "my_tag" tag and "Simple message" message, the flattened log would be as below.

Pattern Flattened log
{d} {l}/{t}: {m} 2016-11-30 13:00:00.000 D/my_tag: Simple message
{d yyyy-MM-dd HH:mm:ss.SSS} {l}/{t}: {m} 2016-11-30 13:00:00.000 D/my_tag: Simple message
{d yyyy/MM/dd HH:mm:ss} {l}|{t}: {m} 2016/11/30 13:00:00 D|my_tag: Simple message
{d yy/MM/dd HH:mm:ss} {l}|{t}: {m} 16/11/30 13:00:00 D|my_tag: Simple message
{d MM/dd HH:mm} {l}-{t}-{m} 11/30 13:00 D-my_tag-Simple message

If you don't even want to construct a pattern, ClassicFlattener is for you. It is a PatternFlattener with a default pattern {d} {l}/{t}: {m}.

By default, FilePrinter use a DefaultFlattener, which just simply concat the timestamp and message together. You may don't like it, so please remember to specify your own Flattener, maybe a ClassicFlattener.

Auto backup

Every single log file may grow to an unexpected size, a AbstractBackupStrategy2 allow you to start a new file at some point, and change the old file name with .bak.n(n is the backup index) suffix.

logs-dir
├──log
├──log.bak.1
├──log.bak.2
├──log.bak.3
├──...
└──log.bak.n

If you don't like the .bak.n suffix, you can use BackupStrategy2 directly to specify the backup file name.

Mostly, you just want to start a new file when the log file reach a specified max-size, so FileSizeBackupStrategy2 is presented for you.

By default, FileSizeBackupStrategy(1024*1024) is used, which will auto backup the log file when it reach a size of 1M. Besides, there will only be one logging file and one backup file in the same time, that means you can save at most only 2M logs.

So, if you want to save more logs, and more backup files, please use FileSizeBackupStrategy2 instead, this allow you keeping multiple backup files in the same time.

Auto clean

If you use a changeable FileNameGenerator, there would be more than one log files in the logs folder, and gets more and more as time goes on. Besides, if you use a backup strategy not limiting the max backup index, that would also make numbers of log files out of control. To prevent running out of disk space, you need a CleanStrategy.

Typically, you can use a FileLastModifiedCleanStrategy, which will delete log files that have not been modified for a period of time(e.g., a week) during initialization.

By default, NeverCleanStrategy is used, which will never do any cleaning.

Compress log files

Just call

LogUtil.compress("<path-to-logs-dir>", "<path-to-zip-file>");

a zip file will be created and the entire log folder will be compressed and written to it, so you can easily collect the user logs for issue-debug.

Note: the origianl log files will not be deleted.

Intercept and filter log

Before each log being printed, you have a chance to modify or filter out the log, by using an Interceptor.

We have already predefined some Interceptor for you, e.g. WhitelistTagsFilterInterceptor only allows the logs of specified tags to be printed, and BlacklistTagsFilterInterceptor is used to filter out(not print) logs of specified tags.

You can specify multiple Interceptors for a single Logger, these Interceptors will be given the opportunity to modify or filter out logs in the order in which they were added. Once a log is filtered out by an Interceptor, subsequent Interceptors will no longer get this log.

Format object

When logging an object

XLog.d(object);

the toString of the object will be called by default.

Sometimes, the toString implementation of the object is not quite what you want, so you need an ObjectFormatter to define how this type of object should be converted to a string when logging.

On the android platform, we predefine IntentFormatter and BundleFormatter for Intent and Bundle class.

You can implement and add your own ObjectFormatter for any class.

Please note, ObjectFormatters only work when logging an object.

Similar libraries

Compare with other logger libraries:

  • Well documented
  • So flexible that you can easily customize or enhance it

Compatibility

In order to be compatible with [Android Log], all the methods of [Android Log] are supported here.

See the Log class defined in [XLog].

Log.v(String, String);
Log.v(String, String, Throwable);
Log.d(String, String);
Log.d(String, String, Throwable);
Log.i(String, String);
Log.i(String, String, Throwable);
Log.w(String, String);
Log.w(String, String, Throwable);
Log.wtf(String, String);
Log.wtf(String, String, Throwable);
Log.e(String, String);
Log.e(String, String, Throwable);
Log.println(int, String, String);
Log.isLoggable(String, int);
Log.getStackTraceString(Throwable);

Migration

If you hav

Extension points exported contracts — how you extend this code

Printer (Interface)
A printer is used for printing the log to somewhere, like android shell, terminal or file system. There are 4 main i [19 …
xlog/src/main/java/com/elvishew/xlog/printer/Printer.java
FileNameGenerator (Interface)
Generates names for log files. [6 implementers]
xlog/src/main/java/com/elvishew/xlog/printer/file/naming/FileNameGenerator.java
BackupStrategy (Interface)
Decide whether the log file should be backup and use a new file for next logging. [7 implementers]
xlog/src/main/java/com/elvishew/xlog/printer/file/backup/BackupStrategy.java
Formatter (Interface)
A formatter is used for format the data that is not a string, or that is a string but not well formatted, we should form [9 …
xlog/src/main/java/com/elvishew/xlog/formatter/Formatter.java
Flattener2 (Interface)
The flattener used to flatten log elements(log time milliseconds, level, tag and message) to a single CharSequence. @si [5 …
xlog/src/main/java/com/elvishew/xlog/flattener/Flattener2.java

Core symbols most depended-on inside this repo

build
called by 58
xlog/src/main/java/com/elvishew/xlog/Logger.java
get
called by 51
xlog/src/main/java/com/elvishew/xlog/internal/Platform.java
assertInitialization
called by 39
xlog/src/main/java/com/elvishew/xlog/XLog.java
backup
called by 38
xlog/src/main/java/com/elvishew/xlog/internal/printer/file/backup/BackupUtil.java
println
called by 30
xlog/src/main/java/com/elvishew/xlog/Logger.java
i
called by 25
xlog/src/main/java/com/elvishew/xlog/XLog.java
log
called by 17
xlog/src/main/java/com/elvishew/xlog/Logger.java
format
called by 17
xlog/src/main/java/com/elvishew/xlog/formatter/Formatter.java

Shape

Method 407
Class 80
Interface 17

Languages

Java100%

Modules by API surface

xlog/src/main/java/com/elvishew/xlog/Logger.java47 symbols
xlog/src/main/java/com/elvishew/xlog/XLog.java45 symbols
xlog/src/main/java/com/elvishew/xlog/LogConfiguration.java32 symbols
xlog/src/main/java/com/elvishew/xlog/flattener/PatternFlattener.java26 symbols
xlog-libcat/src/main/java/com/elvishew/xlog/libcat/internal/LogAspect.java26 symbols
xlog/src/test/java/com/elvishew/xlog/XLogTest.java24 symbols
xlog/src/main/java/com/elvishew/xlog/printer/file/FilePrinter.java24 symbols
xlog/src/main/java/com/elvishew/xlog/internal/DefaultsFactory.java15 symbols
xlog-sample/src/main/java/com/elvishew/xlogsample/RecyclerViewPrinter.java15 symbols
xlog/src/test/java/com/elvishew/xlog/printer/file/backup/BackupTest.java14 symbols
xlog/src/main/java/com/elvishew/xlog/internal/Platform.java14 symbols
xlog-sample/src/main/java/com/elvishew/xlogsample/MainActivity.java13 symbols

For agents

$ claude mcp add xLog \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page