How to Troubleshoot SmsForwarder Message Forwarding Failures Using Logs
SmsForwarder logs every forwarding step via Android WorkManager classes (e.g., SendWorker, UpdateLogsWorker) to both a local Room database (Logs table) and Logcat, allowing you to diagnose failures by inspecting the Logs UI or filtering Logcat for worker-specific tags like SendWorker:E.
SmsForwarder (available at pppscn/SmsForwarder) is an open-source Android application that automates SMS, call, and notification forwarding to multiple channels. When messages fail to reach their configured destinations, the application provides diagnostic visibility through a custom Log wrapper and persistent database entries. Understanding how to access and interpret these logs is essential for resolving silent-period blocks, rule mismatches, and network configuration errors.
Understanding the SmsForwarder Logging Architecture
The Worker-Based Processing Pipeline
SmsForwarder processes incoming events through dedicated Worker classes in the cn.ppps.forwarder.workers package. Each worker handles a specific phase of the forwarding lifecycle and emits detailed debug information via the custom Log utility (wrapping Log.d and Log.e calls).
The primary workers responsible for logging include:
- SendWorker: Handles core forwarding logic, rule matching, silent-period validation, and duplicate filtering
- UpdateLogsWorker: Manages uploading local log entries to remote servers
- NetworkWorker: Validates network state conditions against task requirements
- SimWorker: Verifies SIM card slot and state requirements
- LocationWorker: Checks location-based forwarding conditions
The Logs Database and UI Layer
Every forwarding attempt creates a persistent record in the Logs table defined in cn.ppps.forwarder.database.entity.Logs.kt. The LogsFragment (app/src/main/kotlin/cn/ppps/forwarder/fragment/LogsFragment.kt) provides the main interface for browsing these entries, reading data through the LogsViewModel → LogsRepository → LogsDao chain.
Step-by-Step Troubleshooting Workflow
Follow this systematic approach to identify why a message failed to forward:
-
Enable verbose logging – The app logs at
DEBUGlevel by default. For additional detail, ensureLog.isDebug = trueis set via the app's settings (this flag is managed bySettingUtils). -
Reproduce the failure – Trigger the specific SMS, call, or app notification that should be forwarded to generate fresh log entries.
-
Open the Logs view – Navigate to the Logs screen from the main UI (listed under Message History). This fragment queries the database through
LogsViewModelto display timestamps, sender IDs, and rule associations. -
Filter by tag or task – Each worker logs with a tag equal to its class name. Use the built-in filter UI or search Logcat for specific tags like
SendWorkerorUpdateLogsWorkerto isolate the relevant processing step. -
Interpret key log entries – Examine the specific worker logs for error signatures (detailed in the next section).
-
Check the database entry – Each forward attempt inserts a
Logsrecord viaCore.logs.insert(log). Open the entry in the UI to see the exact failure reason, including optional comments likeResUtils.getString(R.string.silent_time_period)for silent-period blocks. -
Use Logcat for low-level issues – If the UI shows no entry (indicating a crash before DB insertion), attach Android Studio or run
adb logcatfiltered by the package namecn.ppps.forwarder.
Interpreting Worker Log Entries
SendWorker Logs
The SendWorker class (app/src/main/kotlin/cn/ppps/forwarder/workers/SendWorker.kt) logs the complete forwarding pipeline:
- Start of forwarding:
Log.d(TAG, "SendWorker start...") - Silent-period checks: Logs when the current time falls within
SettingUtils.silentPeriodStart/End - Duplicate filtering: Validates against
SettingUtils.duplicateMessagesLimits - Rule matching: Logs
rule.toString()for each evaluated rule before attempting sends
Errors appear as Log.e(TAG, "SendWorker error: …") with specific failure contexts.
UpdateLogsWorker Logs
Located in app/src/main/kotlin/cn/ppps/forwarder/workers/UpdateLogsWorker.kt, this worker handles remote log synchronization:
- Success:
Log.d("UpdateLogsWorker", "UpdateLogsWorker sendResponseJson: …") - Failure:
Log.e("UpdateLogsWorker", "UpdateLogsWorker error: …")(indicates server unreachability or authentication issues)
Condition Workers
NetworkWorker, SimWorker, and LocationWorker log condition validation failures with structured messages like:
Log.d(TAG, "TASK-123:networkState is not match…")
Common Failure Patterns and Log Signatures
| Failure scenario | Log messages | Likely cause |
|---|---|---|
| Silent-period blocking | Log.e(TAG, "免打扰(禁用转发)时间段") |
SettingUtils.silentPeriodStart/End covers the current time and enableSilentPeriodLogs is disabled |
| Duplicate-message filtering | Log.e(TAG, "过滤重复消息机制") |
SettingUtils.duplicateMessagesLimits is too low; identical payload arrived within the deduplication window |
| No matching rule | Log.d(TAG, rule.toString()) followed by Result.failure without Log.e |
Incoming message type or SIM slot does not satisfy any active rule's conditions |
| Condition mismatch | Log.d(TAG, "TASK-…:networkState is not match…") |
The task's network, SIM, or location prerequisites are not met |
| Remote log upload failure | Log.e("UpdateLogsWorker", "UpdateLogsWorker error: …") |
Remote server unreachable or API authentication failure |
Querying Logs Programmatically
When debugging advanced scenarios, you can inspect logs directly via the database layer:
Retrieve Recent Log Entries
// Query the last 20 log entries from the DB
val recentLogs = Core.logs.query(limit = 20, orderDesc = true)
recentLogs.forEach {
Log.d("DebugHelper", "Log ${it.id}: ${it.msg} – sender ${it.senderId}, rule ${it.ruleId}")
}
Manually Trigger Log Upload
Useful when the UI shows "Pending" status for remote log synchronization:
val work = OneTimeWorkRequestBuilder<UpdateLogsWorker>()
.setInputData(workDataOf("logId" to logId))
.build()
WorkManager.getInstance(context).enqueue(work)
Filter Logcat for Specific Workers
# Show only error-level logs from SendWorker
adb logcat -s SendWorker:E
# Filter for all SmsForwarder workers
adb logcat -s SendWorker:D -s UpdateLogsWorker:D -s NetworkWorker:D
Summary
- SmsForwarder uses Worker classes (
SendWorker,UpdateLogsWorker, etc.) to process messages, with each step logged via a customLogwrapper - Failed forwards persist in the Logs database table (
cn.ppps.forwarder.database.entity.Logs) and are viewable through LogsFragment - Enable
Log.isDebug = trueviaSettingUtilsfor verbose output before reproducing the issue - Filter Logcat by worker tags (e.g.,
SendWorker:E) to isolate specific failure points like silent-period blocks or rule mismatches - Common failures include duplicate-message filtering, condition mismatches (network/SIM/location), and remote server upload errors
Frequently Asked Questions
Where are SmsForwarder logs stored?
SmsForwarder stores forwarding history in a local Room database table named Logs (defined in app/src/main/kotlin/cn/ppps/forwarder/database/entity/Logs.kt). The UI accesses this data through LogsRepository to display entries in LogsFragment. Additionally, debug logs are emitted to Android Logcat with worker-specific tags like SendWorker and UpdateLogsWorker.
How do I enable debug logging in SmsForwarder?
Debug logging is controlled by the Log.isDebug flag, which is read from SettingUtils in the app's settings. When enabled, workers emit detailed Log.d statements showing rule evaluation, condition checks, and HTTP request/response details. For command-line debugging, use adb logcat -s SendWorker:D to capture debug-level output from the forwarding worker.
Why does my message show "Silent period" in the logs?
This indicates the current time falls within the silent period configured in SettingUtils.silentPeriodStart and SettingUtils.silentPeriodEnd. When active and enableSilentPeriodLogs is disabled, SendWorker logs Log.e(TAG, "免打扰(禁用转发)时间段") and aborts forwarding. Adjust the time window in settings or disable the silent period feature to allow forwarding during those hours.
How can I check why a specific rule didn't match?
SendWorker logs each rule evaluation via Log.d(TAG, rule.toString()) before attempting to match. If no rule matches, you will see the rule dump followed by a Result.failure without an accompanying Log.e. Compare the logged rule conditions (SIM slot, sender pattern, content regex) against the incoming message metadata visible in the Logs UI to identify the mismatch.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →