Skip to main content

Logging

When something goes wrong (and it will), logs are your best friend. The SDK uses SLF4J with Logback-Android — industry standards that give you full control over what gets logged and where.


Why Use Logging?

Logging is essential for:

  • Debugging — Track down issues during development
  • Monitoring — Understand application behavior in production
  • Troubleshooting — Diagnose problems reported by users
  • Auditing — Keep records of device operations

Quick Setup

1. Add Dependencies

In your app/build.gradle:

dependencies {
// SLF4J API
implementation 'org.slf4j:slf4j-api:2.0.17'

// Logback-Android implementation
implementation 'com.github.tony19:logback-android:3.0.0'

// ... your other dependencies
}

2. Create the Config File

Create app/src/main/assets/logback.xml:

<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://tony19.github.io/logback-android/xml"
xsi:schemaLocation="https://tony19.github.io/logback-android/xml https://cdn.jsdelivr.net/gh/tony19/logback-android/logback.xsd">

<appender name="logcat" class="ch.qos.logback.classic.android.LogcatAppender">
<tagEncoder>
<pattern>%logger{12}</pattern>
</tagEncoder>
<encoder>
<pattern>[%-20thread] %msg</pattern>
</encoder>
</appender>

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOGS_DIR}/app.log</file>

<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOGS_DIR}/app.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>1MB</maxFileSize>
<maxHistory>100</maxHistory>
</rollingPolicy>

<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<!-- Set Log Level of specific package -->
<logger name="com.example.sdk" level="INFO" />

<root level="DEBUG">
<appender-ref ref="logcat" />
<appender-ref ref="FILE" />
</root>
</configuration>

3. Initialize the Log Directory

In your Application class:

public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();

// Set log directory BEFORE any logging happens
System.setProperty("LOGS_DIR", getFilesDir().getAbsolutePath() + "/logs");
}
}

Register it in AndroidManifest.xml:

<application
android:name=".MyApplication"
... >

Done. Logging is ready.


Using Loggers

Create a Logger

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

public class PrintManager {
private final Logger logger = LoggerFactory.getLogger(PrintManager.class);

// Now use logger.debug(), logger.info(), etc.
}

Log Levels

LevelWhen to UseExample
debugDetailed diagnostic infoVariable values, method calls
infoNormal operationsSuccessful connections, state changes
warnSomething unusualDeprecated API usage, recoverable errors
errorSomething failedFailed operations, caught exceptions

Example Usage

public class DeviceOperations {
private final Logger logger = LoggerFactory.getLogger(DeviceOperations.class);

public void connectToDevice(DeviceConnection device) {
logger.debug("Attempting connection to: {}", device.getName());

deviceAdapter.connect()
.thenAccept(unused -> {
logger.info("Successfully connected to {}", device.getName());
})
.exceptionally(error -> {
logger.error("Connection failed for device: {}", device.getName(), error);
return null;
});
}

public void printLabel(String template, String data, int copies) {
logger.debug("Print request: template={}, copies={}", template, copies);

if (copies > 100) {
logger.warn("Large print job requested: {} copies", copies);
}

printerAdapter.print(template, data, copies)
.thenAccept(unused -> logger.info("Print job completed: {} copies", copies))
.exceptionally(error -> {
logger.error("Print failed: template={}", template, error);
return null;
});
}
}

Best Practices

1. Use Parameterized Logging

// ✅ Good — parameters only evaluated if level is enabled
logger.debug("Printing {} copies of {}", copies, templateName);

// ❌ Avoid — string concatenation always happens
logger.debug("Printing " + copies + " copies of " + templateName);

2. Log Exceptions Properly

Pass the exception as the last parameter for full stack traces:

// ✅ Good — full stack trace in logs
logger.error("Operation failed for device {}", deviceName, exception);

// ❌ Avoid — loses stack trace
logger.error("Operation failed: " + exception.getMessage());

3. Include Context

// ✅ Good — includes context
logger.error("Failed to print label '{}' on printer '{}'",
labelName, printerName, exception);

// ❌ Avoid — missing context
logger.error("Print failed", exception);

4. Don't Log Sensitive Data

// ❌ Avoid — don't log passwords, tokens, personal data
logger.debug("Login with username: {} and password: {}", username, password);

// ✅ Good — log safe information only
logger.debug("Login attempt for user: {}", username);

5. Use Appropriate Levels

// ✅ Good — appropriate levels
logger.debug("Processing item {} of {}", current, total);
logger.info("Connection established");
logger.warn("Retrying connection, attempt {}", attempt);
logger.error("Failed to connect", exception);

// ❌ Avoid — everything as ERROR
logger.error("Processing item"); // Should be DEBUG

Configuration Options

Log Levels

Control verbosity in logback.xml:

<!-- See everything from the SDK -->
<logger name="com.averydennison.addevicemanager" level="DEBUG" />

<!-- Only errors from noisy libraries -->
<logger name="some.chatty.library" level="ERROR" />

<!-- Default level for everything else -->
<root level="INFO">
<appender-ref ref="LOGCAT" />
</root>

Level hierarchy: DEBUG < INFO < WARN < ERROR

Setting level to INFO means you see INFO, WARN, and ERROR — but not DEBUG.

File Rotation

Control how logs are stored:

<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- Daily rotation with index for size overflow -->
<fileNamePattern>${LOGS_DIR}/app.%d{yyyy-MM-dd}.%i.log</fileNamePattern>

<!-- Split file when it hits 1MB -->
<maxFileSize>1MB</maxFileSize>

<!-- Keep 7 days of logs -->
<maxHistory>7</maxHistory>

<!-- Never use more than 10MB total -->
<totalSizeCap>10MB</totalSizeCap>
</rollingPolicy>

Log Format

Customize what each line looks like:

<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>

Pattern elements:

  • %d{...} — Date/time format
  • %thread — Thread name
  • %-5level — Log level (padded to 5 characters)
  • %logger{36} — Logger name (truncated to 36 characters)
  • %msg — Log message
  • %n — Line separator

Accessing Log Files

Log files are stored in the app's private storage:

File logsDir = new File(getFilesDir(), "logs");
File[] logFiles = logsDir.listFiles();

if (logFiles != null) {
for (File logFile : logFiles) {
logger.info("Log file: {}, size: {} bytes",
logFile.getName(), logFile.length());
}
}

Sharing Log Files

To share logs with support teams:

public void shareLogs() {
File logsDir = new File(getFilesDir(), "logs");
File latestLog = new File(logsDir, "app.log");

if (latestLog.exists()) {
Uri logUri = FileProvider.getUriForFile(
this,
"com.example.myapp.fileprovider",
latestLog
);

Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_STREAM, logUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(shareIntent, "Share logs"));
}
}

Troubleshooting

Logs Not Appearing

  1. Check log level — Ensure your logger level allows the message:

    <root level="DEBUG">  <!-- Must be DEBUG or lower to see debug messages -->
  2. Verify logback.xml — Ensure file is in src/main/assets/

  3. Check Application class — Verify LOGS_DIR is set before any logging

Log Files Not Created

  1. Check directory — Verify the directory path exists and is writable:

    File logsDir = new File(getFilesDir(), "logs");
    if (!logsDir.exists()) {
    boolean created = logsDir.mkdirs();
    logger.info("Logs directory created: {}", created);
    }
  2. Verify property — Ensure LOGS_DIR system property is set in Application.onCreate()

Performance Issues

If logging impacts performance:

  1. Reduce log level in production (INFO or WARN)
  2. Limit file size and history in logback.xml
  3. Use async appender for file logging:
    <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="FILE" />
    </appender>

Next Steps

GuideWhat You'll Learn
Getting StartedSDK setup and initialization
CallbacksMonitor SDK events
Error HandlingHandle failures gracefully
TelemetryDevice analytics and usage data

For more information about Logback-Android, visit: https://github.com/tony19/logback-android