@ably/cli
Version:
Ably CLI for Pub/Sub, Chat and Spaces
162 lines (161 loc) • 7.02 kB
JavaScript
import { Flags } from "@oclif/core";
import chalk from "chalk";
import { AblyBaseCommand } from "../../../base-command.js";
import { formatJson, isJsonData } from "../../../utils/json-formatter.js";
export default class LogsPushSubscribe extends AblyBaseCommand {
static description = "Stream logs from the push notifications meta channel [meta]log:push";
static examples = [
"$ ably logs push subscribe",
"$ ably logs push subscribe --rewind 10",
];
static flags = {
...AblyBaseCommand.globalFlags,
json: Flags.boolean({
default: false,
description: "Output results as JSON",
}),
rewind: Flags.integer({
default: 0,
description: "Number of messages to rewind when subscribing",
}),
};
client = null;
// Override finally to ensure resources are cleaned up
async finally(err) {
if (this.client &&
this.client.connection.state !== "closed" && // Check state before closing to avoid errors if already closed
this.client.connection.state !== "failed") {
this.client.close();
}
return super.finally(err);
}
async run() {
const { flags } = await this.parse(LogsPushSubscribe);
try {
// Create the Ably client
this.client = await this.createAblyRealtimeClient(flags);
if (!this.client)
return;
const { client } = this; // local const
const channelName = "[meta]log:push";
const channelOptions = {};
// Set up connection state logging
this.setupConnectionStateLogging(client, flags, {
includeUserFriendlyMessages: true,
});
// Configure rewind if specified
if (flags.rewind > 0) {
this.logCliEvent(flags, "logs", "rewindEnabled", `Rewind enabled for ${channelName}`, { channel: channelName, count: flags.rewind });
channelOptions.params = {
...channelOptions.params,
rewind: flags.rewind.toString(),
};
}
const channel = client.channels.get(channelName, channelOptions);
// Set up channel state logging
this.setupChannelStateLogging(channel, flags, {
includeUserFriendlyMessages: true,
});
this.logCliEvent(flags, "logs", "subscribing", `Subscribing to ${channelName}...`);
if (!this.shouldOutputJson(flags)) {
this.log(`Subscribing to ${chalk.cyan(channelName)}...`);
this.log("Press Ctrl+C to exit");
this.log("");
}
// Subscribe to the channel
channel.subscribe((message) => {
const timestamp = message.timestamp
? new Date(message.timestamp).toISOString()
: new Date().toISOString();
const event = message.name || "unknown";
const logEvent = {
channel: channelName,
data: message.data,
event,
timestamp,
};
this.logCliEvent(flags, "logs", "logReceived", `Log received on ${channelName}`, logEvent);
if (this.shouldOutputJson(flags)) {
this.log(this.formatJsonOutput(logEvent, flags));
return;
}
// Color-code different event types based on severity
let eventColor = chalk.blue;
// For push log events - based on examples and severity
if (message.data &&
typeof message.data === "object" &&
"severity" in message.data) {
const severity = message.data.severity;
switch (severity) {
case "error": {
eventColor = chalk.red;
break;
}
case "warning": {
eventColor = chalk.yellow;
break;
}
case "info": {
eventColor = chalk.green;
break;
}
case "debug": {
eventColor = chalk.blue;
break;
}
// No default
}
}
// Format the log output
this.log(`${chalk.dim(`[${timestamp}]`)} Channel: ${chalk.cyan(channelName)} | Event: ${eventColor(event)}`);
if (message.data) {
if (isJsonData(message.data)) {
this.log("Data:");
this.log(formatJson(message.data));
}
else {
this.log(`Data: ${message.data}`);
}
}
this.log("");
});
this.logCliEvent(flags, "logs", "subscribed", `Successfully subscribed to ${channelName}`);
// Set up cleanup for when the process is terminated
const cleanup = () => {
this.logCliEvent(flags, "logs", "cleanupInitiated", "Cleanup initiated (Ctrl+C pressed)");
if (client) {
this.logCliEvent(flags, "connection", "closing", "Closing Ably connection.");
client.close();
this.logCliEvent(flags, "connection", "closed", "Ably connection closed.");
}
};
// Handle process termination
process.on("SIGINT", () => {
if (!this.shouldOutputJson(flags)) {
this.log("\nSubscription ended");
}
cleanup();
process.exit(0); // Reinstated: Explicit exit on signal
});
process.on("SIGTERM", () => {
cleanup();
process.exit(0); // Reinstated: Explicit exit on signal
});
this.logCliEvent(flags, "logs", "listening", "Listening for logs...");
// Wait indefinitely
await new Promise(() => { });
}
catch (error) {
const err = error;
this.logCliEvent(flags, "logs", "fatalError", `Error during log subscription: ${err.message}`, { error: err.message });
this.error(err.message);
}
finally {
// Ensure client is closed
if (this.client && this.client.connection.state !== "closed") {
this.logCliEvent(flags || {}, "connection", "finalCloseAttempt", "Ensuring connection is closed in finally block.");
this.client.close();
}
}
}
}