feat: add privacy-safe server diagnostics
This commit is contained in:
1 parent
2360506607
commit
a3963ad0f6
27 files changed
+1305
-112
No files matched your search
@@ -8,6 +8,7 @@ import {
|
||||
type AgentBusSocketFactory
|
||||
} from './agentbus.js';
|
||||
import { TaskError, type TaskContext } from './task-service.js';
|
||||
import { diagnosticError, diagnosticMetadataKeys } from './diagnostics.js';
|
||||
|
||||
export interface PublicAgentBusChannel {
|
||||
id: string;
|
||||
@@ -120,7 +121,22 @@ export function mergeRuntimeChannelStatuses(
|
||||
export class AgentBusChannelService {
|
||||
private readonly runtimeStatusWrites = new Map<string, Promise<void>>();
|
||||
|
||||
constructor(private readonly config: AppConfig) {}
|
||||
constructor(
|
||||
private readonly config: AppConfig,
|
||||
private readonly logger?: AgentBusLogger
|
||||
) {}
|
||||
|
||||
private log(
|
||||
level: 'info' | 'warn' | 'error',
|
||||
metadata: Record<string, unknown>,
|
||||
message: string
|
||||
): void {
|
||||
try {
|
||||
this.logger?.[level](metadata, message);
|
||||
} catch {
|
||||
// Channel persistence and encryption never depend on operational logs.
|
||||
}
|
||||
}
|
||||
|
||||
private legacyEnvironmentManaged(): boolean {
|
||||
return this.config.agentBusEnabled
|
||||
@@ -178,6 +194,13 @@ export class AgentBusChannelService {
|
||||
try {
|
||||
const wsToken = decryptText(this.config, text(row.agentbus_ws_token_ciphertext));
|
||||
if (!wsUrl || !wsToken || !botAddress) {
|
||||
this.log('warn', {
|
||||
agentbus_event: 'channel_configuration_incomplete',
|
||||
channel_id: text(row.id),
|
||||
ws_url_present: Boolean(wsUrl),
|
||||
ws_token_present: Boolean(wsToken),
|
||||
bot_address_present: Boolean(botAddress)
|
||||
}, 'AgentBus channel configuration is incomplete');
|
||||
await this.setRuntimeStatus(
|
||||
organizationId,
|
||||
text(row.id),
|
||||
@@ -192,7 +215,12 @@ export class AgentBusChannelService {
|
||||
ws_token: wsToken,
|
||||
bot_address: botAddress
|
||||
});
|
||||
} catch {
|
||||
} catch (error) {
|
||||
this.log('error', {
|
||||
agentbus_event: 'channel_key_decryption_failed',
|
||||
channel_id: text(row.id),
|
||||
...diagnosticError(error, 'agentbus_channel_key_decryption_failed')
|
||||
}, 'AgentBus channel key decryption failed');
|
||||
await this.setRuntimeStatus(organizationId, text(row.id), 'error', '渠道 key 无法解密。');
|
||||
}
|
||||
}
|
||||
@@ -471,6 +499,15 @@ export class AgentBusChannelService {
|
||||
VALUES ($1, $2, $3, 'user_channel', $4, $5, $6)`,
|
||||
[context.organizationId, context.userId || null, eventType, entityId, context.requestId, metadata]
|
||||
);
|
||||
this.log('info', {
|
||||
agentbus_event: 'channel_audit_staged',
|
||||
request_id: context.requestId,
|
||||
domain_event: eventType,
|
||||
entity_type: 'user_channel',
|
||||
entity_id: entityId,
|
||||
actor_present: Boolean(context.userId),
|
||||
metadata_keys: diagnosticMetadataKeys(metadata)
|
||||
}, 'AgentBus channel audit event staged');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,7 +530,19 @@ export class AgentBusManager {
|
||||
|
||||
constructor(options: AgentBusManagerOptions) {
|
||||
this.options = options;
|
||||
this.channels = new AgentBusChannelService(options.config);
|
||||
this.channels = new AgentBusChannelService(options.config, options.logger);
|
||||
}
|
||||
|
||||
private log(
|
||||
level: 'info' | 'warn' | 'error',
|
||||
metadata: Record<string, unknown>,
|
||||
message: string
|
||||
): void {
|
||||
try {
|
||||
this.options.logger?.[level](metadata, message);
|
||||
} catch {
|
||||
// Runtime channel management must not depend on log delivery.
|
||||
}
|
||||
}
|
||||
|
||||
get channelService(): AgentBusChannelService {
|
||||
@@ -504,12 +553,18 @@ export class AgentBusManager {
|
||||
if (!this.options.config.agentBusEnabled) return;
|
||||
if (this.started) return;
|
||||
this.started = true;
|
||||
this.log('info', { agentbus_event: 'manager_starting' }, 'AgentBus manager starting');
|
||||
await this.reload();
|
||||
}
|
||||
|
||||
async reload(): Promise<void> {
|
||||
if (!this.started) return;
|
||||
this.reloadRequested = true;
|
||||
this.log('info', {
|
||||
agentbus_event: 'manager_reload_requested',
|
||||
reload_in_flight: Boolean(this.reloadInFlight),
|
||||
current_listeners: this.listeners.size
|
||||
}, 'AgentBus manager reload requested');
|
||||
if (this.reloadInFlight) return this.reloadInFlight;
|
||||
this.reloadInFlight = (async () => {
|
||||
while (this.started && this.reloadRequested) {
|
||||
@@ -518,6 +573,10 @@ export class AgentBusManager {
|
||||
for (const listener of this.listeners.values()) listener.stop(false);
|
||||
this.listeners.clear();
|
||||
const secrets = await this.channels.listEnabledSecrets(this.options.organizationId);
|
||||
this.log('info', {
|
||||
agentbus_event: 'manager_channels_loaded',
|
||||
enabled_channels: secrets.length
|
||||
}, 'AgentBus enabled channels loaded');
|
||||
for (const channel of secrets) {
|
||||
const listener = new AgentBusListener({
|
||||
config: this.options.config,
|
||||
@@ -539,15 +598,36 @@ export class AgentBusManager {
|
||||
status,
|
||||
error,
|
||||
epoch
|
||||
).catch(() => undefined)
|
||||
).catch((statusError) => {
|
||||
this.log('warn', {
|
||||
agentbus_event: 'channel_status_persist_failed',
|
||||
channel_id: channel.id,
|
||||
runtime_status: status,
|
||||
session_epoch: epoch,
|
||||
...diagnosticError(statusError, 'agentbus_channel_status_persist_failed')
|
||||
}, 'AgentBus channel status persistence failed');
|
||||
})
|
||||
});
|
||||
this.listeners.set(channel.id, listener);
|
||||
listener.start();
|
||||
}
|
||||
this.log('info', {
|
||||
agentbus_event: 'manager_reload_completed',
|
||||
active_listeners: this.listeners.size,
|
||||
reload_requested_again: this.reloadRequested
|
||||
}, 'AgentBus manager reload completed');
|
||||
}
|
||||
})().finally(() => {
|
||||
this.reloadInFlight = null;
|
||||
});
|
||||
})()
|
||||
.catch((error) => {
|
||||
this.log('error', {
|
||||
agentbus_event: 'manager_reload_failed',
|
||||
...diagnosticError(error, 'agentbus_manager_reload_failed')
|
||||
}, 'AgentBus manager reload failed');
|
||||
throw error;
|
||||
})
|
||||
.finally(() => {
|
||||
this.reloadInFlight = null;
|
||||
});
|
||||
return this.reloadInFlight;
|
||||
}
|
||||
|
||||
@@ -559,6 +639,7 @@ export class AgentBusManager {
|
||||
// an older process write `disabled` after a newer process has connected.
|
||||
for (const listener of this.listeners.values()) listener.stop(false);
|
||||
this.listeners.clear();
|
||||
this.log('info', { agentbus_event: 'manager_stopped' }, 'AgentBus manager stopped');
|
||||
}
|
||||
|
||||
status(): {
|
||||
|
||||
Reference in new issue
Block a user