Log level for verbosity (error, warn, info, debug, trace)
When working with the Zoom Realtime Media Streams (RTMS) SDK, understanding how to control logging is essential for debugging, monitoring, and integrating with your existing logging infrastructure. This guide covers everything you need to know about RTMS SDK logging.
Quick start with environment variables
The RTMS Quickstart app provides a .env.example file that demonstrates the three key logging parameters:
# Log level for verbosity (error, warn, info, debug, trace)
ZM_RTMS_LOG_LEVEL=info
# Log format (progressive for human-readable, json for machine-readable)
ZM_RTMS_LOG_FORMAT=progressive
# Enable or disable logging
ZM_RTMS_LOG_ENABLED=true
Simply copy .env.example to .env and configure these values before running your application.
Understanding log levels
The RTMS SDK supports five log levels, from least to most verbose:
| Level | Value | Use Case |
|---|---|---|
error | 0 | Production - critical errors only |
warn | 1 | Production - errors and warnings |
info | 2 | Development - general information (default) |
debug | 3 | Debugging - detailed diagnostic info |
trace | 4 | Deep debugging - includes all SDK operations |
Log format options
Progressive format (human-readable)
Best for development and console output:
ZM_RTMS_LOG_FORMAT=progressive
Output example:
rtms | 2025-10-03T10:15:30.123Z | INFO | Connecting to meeting
rtms | 2025-10-03T10:15:31.456Z | DEBUG | WebSocket connection established
JSON format (machine-readable)
Ideal for log aggregation systems like Elasticsearch, Splunk, or CloudWatch:
ZM_RTMS_LOG_FORMAT=json
Output example:
{"timestamp":"2025-10-03T10:15:30.123Z","level":"info","component":"rtms","message":"Connecting to meeting"}
{"timestamp":"2025-10-03T10:15:31.456Z","level":"debug","component":"rtms","message":"WebSocket connection established"}
Redirecting SDK logs
The RTMS SDK writes to stdout. Use standard shell redirection to send logs to files or processing pipelines:
Redirect to file
# All output to file
npm start > rtms.log 2>&1
# Only stdout (SDK logs) to file
npm start > rtms.log
# Separate stdout and stderr
npm start > rtms.log 2> errors.log
Pipe to log processor
# Send to journald
npm start | systemd-cat -t rtms-app
# Process with jq (for JSON format)
npm start | jq -r '.message'
# Stream to remote syslog
npm start | nc syslog-server 514
Dockerized applications
In your Dockerfile or docker-compose.yml:
services:
rtms-app:
build: .
environment:
- ZM_RTMS_LOG_LEVEL=info
- ZM_RTMS_LOG_FORMAT=json
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
Programmatic configuration
Override environment variables at runtime using the configureLogger() function:
import rtms from "@zoom/rtms";
// Configure logging programmatically
rtms.configureLogger({
level: rtms.LogLevel.DEBUG,
format: rtms.LogFormat.JSON,
enabled: true,
});
This is useful for:
- Dynamic log level adjustment based on runtime conditions
- Different logging configurations per environment
- Testing scenarios requiring specific log outputs
Disabling SDK logging for custom solutions
If you prefer to use your own logging framework (Winston, Pino, Bunyan, etc.), disable the SDK's internal logging and implement logging in the callbacks:
Step 1: Disable SDK logging
# In .env
ZM_RTMS_LOG_ENABLED=false
Or programmatically:
rtms.configureLogger({ enabled: false });
Step 2: Implement custom logging in callbacks
Here's a complete example using a custom logger:
import rtms from "@zoom/rtms";
import winston from "winston";
// Set up your custom logger
const logger = winston.createLogger({
level: "info",
format: winston.format.json(),
transports: [
new winston.transports.File({
filename: "rtms-error.log",
level: "error",
}),
new winston.transports.File({ filename: "rtms-combined.log" }),
],
});
// Disable SDK logging
rtms.configureLogger({ enabled: false });
const client = new rtms.Client();
// Log join confirmation
client.onJoinConfirm((reason) => {
logger.info("Meeting joined", {
reason,
timestamp: Date.now(),
});
});
// Log session updates
client.onSessionUpdate((op, sessionInfo) => {
logger.info("Session update", {
operation: op,
sessionId: sessionInfo.sessionId,
status: sessionInfo.status,
});
});
// Log participant changes
client.onUserUpdate((op, participantInfo) => {
logger.info("User update", {
operation: op,
userId: participantInfo.userId,
userName: participantInfo.userName,
eventType: op === rtms.USER_EVENT_JOIN ? "join" : "leave",
});
});
// Log media data with metrics
client.onAudioData((buffer, size, timestamp, metadata) => {
logger.debug("Audio data received", {
userName: metadata.userName,
userId: metadata.userId,
size,
timestamp,
});
});
client.onVideoData((buffer, size, timestamp, trackId, metadata) => {
logger.debug("Video data received", {
userName: metadata.userName,
trackId,
size,
timestamp,
});
});
client.onTranscriptData((buffer, size, timestamp, metadata) => {
const text = buffer.toString("utf8");
logger.info("Transcript received", {
userName: metadata.userName,
text,
timestamp,
});
});
// Log disconnection
client.onLeave((reason) => {
logger.warn("Meeting ended", {
reason,
timestamp: Date.now(),
});
});
Production best practices
1. Use JSON format for production
ZM_RTMS_LOG_FORMAT=json
ZM_RTMS_LOG_LEVEL=warn
JSON logs integrate seamlessly with log aggregation platforms and make querying much easier.
2. Implement structured logging
Add context to your callback logs:
client.onAudioData((buffer, size, timestamp, metadata) => {
logger.info({
event: "audio_received",
meeting_id: client.uuid(),
stream_id: client.streamId(),
participant: {
user_id: metadata.userId,
user_name: metadata.userName,
},
metrics: {
size_bytes: size,
timestamp: timestamp,
},
});
});
3. Monitor key metrics
Track important events for monitoring:
const metrics = {
audioFrames: 0,
videoFrames: 0,
participants: new Set(),
};
client.onAudioData((buffer, size, timestamp, metadata) => {
metrics.audioFrames++;
metrics.participants.add(metadata.userId);
});
// Log metrics periodically
setInterval(() => {
logger.info("RTMS metrics", metrics);
metrics.audioFrames = 0;
metrics.videoFrames = 0;
}, 60000); // Every minute
4. Handle errors gracefully
Always log error scenarios:
client.onLeave((reason) => {
if (reason !== 0) {
logger.error("Abnormal disconnect", {
reason_code: reason,
meeting_id: client.uuid(),
});
// Implement reconnection logic here
}
});
Debugging tips
Enable trace logging temporarily
// Enable detailed logging for debugging
if (process.env.NODE_ENV !== "production") {
rtms.configureLogger({
level: rtms.LogLevel.TRACE,
format: rtms.LogFormat.PROGRESSIVE,
});
}
Filter logs by component
When using JSON format, filter by component:
npm start | jq 'select(.component == "rtms")'
Correlate logs with meeting IDs
Always include meeting context:
const meetingContext = {
uuid: client.uuid(),
streamId: client.streamId(),
};
logger.info("Processing started", meetingContext);
Resources
- RTMS SDK Repository - Core SDK documentation
- RTMS Quickstart App - Example implementation
- RTMS Developer Documentation - Example implementation
- Zoom Developer Forum - Community support and discussions
Summary
The RTMS SDK provides flexible logging options through environment variables or programmatic configuration. For production use, disable SDK logging and implement custom logging within the callback functions to maintain full control over your logging infrastructure. This approach allows you to:
- Integrate with existing logging frameworks
- Add business-specific context to logs
- Control log retention and rotation policies
- Implement custom alerting and monitoring
Need help? Join the conversation on the Zoom Developer Forum!