The Messaging SDK uses LogTape for structured logging. Logging is completely optional - the SDK works perfectly without it.
If you want logging, install LogTape:
npm install @logtape/logtape
# or
pnpm add @logtape/logtape
# or
yarn add @logtape/logtapeConfigure LogTape once at application startup:
import { configure, getConsoleSink } from "@logtape/logtape";
import { LOG_CATEGORIES } from "@mysten/messaging";
// Configure LogTape before using the Messaging SDK
await configure({
sinks: {
console: getConsoleSink(),
},
filters: {},
loggers: [
// Enable all messaging SDK logs at info level
{
category: LOG_CATEGORIES.ROOT,
lowestLevel: "info",
sinks: ["console"],
},
],
});Tips:
- Use
LOG_CATEGORIESconstants: Import and use theLOG_CATEGORIESconstants instead of manually writing category arrays (e.g.,["@mysten/messaging"]). This prevents typos and provides better IDE autocomplete. - Production logging: For production environments, consider using the JSON Lines formatter for machine-readable structured logs:
This outputs one JSON object per line, making it easy to integrate with log aggregation systems like ELK, Datadog, or CloudWatch.
import { getConsoleSink, getJsonLinesFormatter } from "@logtape/logtape"; const console = getConsoleSink({ formatter: getJsonLinesFormatter() });
The SDK uses a hierarchical category structure for fine-grained control:
| Constant | Category | Description | Typical Operations |
|---|---|---|---|
LOG_CATEGORIES.ROOT |
["@mysten/messaging"] |
Root - captures all SDK logs | All operations |
LOG_CATEGORIES.CLIENT_READS |
["@mysten/messaging", "client", "reads"] |
Read operations | getChannelObjects, getChannelMessages, getChannelMembers |
LOG_CATEGORIES.CLIENT_WRITES |
["@mysten/messaging", "client", "writes"] |
Write operations | executeCreateChannel, executeSendMessage, executeAddMembers |
LOG_CATEGORIES.ENCRYPTION |
["@mysten/messaging", "encryption"] |
Encryption operations | Key generation, encrypt/decrypt operations |
LOG_CATEGORIES.STORAGE |
["@mysten/messaging", "storage"] |
All storage operations | Upload/download to storage adapters |
LOG_CATEGORIES.STORAGE_WALRUS |
["@mysten/messaging", "storage", "walrus"] |
Walrus-specific operations | Walrus uploads, downloads, errors |
Recommended: Use the LOG_CATEGORIES constants (imported from "@mysten/messaging") in your configuration for type safety and autocompletion.
The SDK uses four log levels:
debug: Detailed diagnostic information (entry points, parameters)info: Successful operations with key identifiers (channelId, messageId, etc.)warning: Unexpected but handled situations (partial failures, retries)error: Operation failures with error context
Log everything at debug level for maximum visibility:
import { LOG_CATEGORIES } from "@mysten/messaging";
await configure({
sinks: {
console: getConsoleSink(),
},
filters: {},
loggers: [
{
category: LOG_CATEGORIES.ROOT,
lowestLevel: "debug",
sinks: ["console"],
},
],
});Log only errors to minimize noise:
import { LOG_CATEGORIES } from "@mysten/messaging";
await configure({
sinks: {
console: getConsoleSink(),
},
filters: {},
loggers: [
{
category: LOG_CATEGORIES.ROOT,
lowestLevel: "error",
sinks: ["console"],
},
],
});Enable debug logging for specific modules:
import { LOG_CATEGORIES } from "@mysten/messaging";
await configure({
sinks: {
console: getConsoleSink(),
},
filters: {},
loggers: [
// Info for all SDK operations
{
category: LOG_CATEGORIES.ROOT,
lowestLevel: "info",
sinks: ["console"],
},
// Debug for encryption troubleshooting
{
category: LOG_CATEGORIES.ENCRYPTION,
lowestLevel: "debug",
sinks: ["console"],
},
// Debug for storage troubleshooting
{
category: LOG_CATEGORIES.STORAGE_WALRUS,
lowestLevel: "debug",
sinks: ["console"],
},
],
});Send different log levels to different destinations:
import { configure, getConsoleSink, getFileSink } from "@logtape/logtape";
import { LOG_CATEGORIES } from "@mysten/messaging";
await configure({
sinks: {
console: getConsoleSink(),
errorFile: getFileSink("errors.log"),
},
filters: {},
loggers: [
// All logs to console
{
category: LOG_CATEGORIES.ROOT,
lowestLevel: "info",
sinks: ["console"],
},
// Errors to file
{
category: LOG_CATEGORIES.ROOT,
lowestLevel: "error",
sinks: ["errorFile"],
},
],
});Debug level:
- Entry parameters (channelId, userAddress, cursor, limit)
- Query details
Info level:
- Retrieved channelIds
- Message counts and pagination state
- MemberCap IDs
Example:
{
"level": "info",
"category": ["@mysten/messaging", "client", "reads"],
"message": "Retrieved channel messages",
"properties": {
"channelId": "0x...",
"messagesTableId": "0x...",
"messageCount": 10,
"fetchRange": "0-10",
"cursor": 10,
"hasNextPage": true,
"direction": "backward"
}
}Debug level:
- Operation parameters (addresses, counts)
- Transaction building details
Info level:
- Created object IDs (channelId, messageId, creatorCapId)
- Transaction digests
- Member counts
Example:
{
"level": "info",
"category": ["@mysten/messaging", "client", "writes"],
"message": "Channel created",
"properties": {
"channelId": "0x...",
"creatorCapId": "0x...",
"creatorAddress": "0x...",
"memberCount": 3,
"digest": "0x..."
}
}Debug level:
- Key generation events
- Encryption/decryption operations with payload sizes
- No sensitive data (keys or decrypted content)
Debug level:
- Upload/download initiation with counts and URLs
- Blob IDs and sizes
Info level:
- Successful uploads with blob IDs
- Download completion with byte counts
Error level:
- Upload failures with HTTP status and error text
- Network errors
The SDK never logs:
- Raw encryption keys (session keys, symmetric keys, private keys)
- Decrypted message content
- Decrypted attachment data
- Private key material
The SDK does log:
- Object IDs (channels, messages, member caps) - these are public on-chain
- Sender and member addresses - these are public on-chain
- Payload lengths (not the actual content)
- Operation metadata (counts, timestamps, blob IDs)
- Error messages (from
Error.message) - may contain sensitive information in stack traces
Important: Error messages are logged as-is from caught exceptions. Review your error logs to ensure no sensitive data is inadvertently exposed through error messages. Consider using LogTape's redaction features if you need to sanitize logs before sending to external systems.
-
Verify LogTape is configured:
await configure({ /* ... */ });
Call this before using the Messaging SDK.
-
Check log level: Ensure the category's
lowestLevelis low enough to capture logs. Example:"debug"captures everything,"error"only errors. -
Verify category matches: Use
LOG_CATEGORIES.ROOT(or["@mysten/messaging"]) to capture all SDK logs.
-
Increase log level: Change from
"debug"to"info"or"warning". -
Add filters:
{ category: ["@mysten/messaging"], level: "info", filters: [(record) => record.properties.channelId === "0x..."], sinks: ["console"], }
-
Target specific categories: Only enable logging for modules you're debugging.
For advanced LogTape features such as:
- Request tracing with implicit contexts
- Custom sinks and formatters
- Data redaction
- Integration with monitoring systems
- Performance optimization
Please refer to the LogTape Documentation.