Skip to content

Commit

Permalink
feat(agent&vscode): add support for agent log to vscode output. (Tabb…
Browse files Browse the repository at this point in the history
  • Loading branch information
icycodes authored Feb 29, 2024
1 parent ce90188 commit 2b7a303
Show file tree
Hide file tree
Showing 18 changed files with 161 additions and 53 deletions.
2 changes: 2 additions & 0 deletions clients/tabby-agent/src/Agent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { components as ApiComponents } from "./types/tabbyApi";
import type { AgentConfig, PartialAgentConfig } from "./AgentConfig";
import type { DataStore } from "./dataStore";
import type { Logger } from "./logger";
import type { CompletionRequest, CompletionResponse } from "./CompletionContext";

export type { CompletionRequest, CompletionResponse };
Expand All @@ -14,6 +15,7 @@ export type AgentInitOptions = Partial<{
config: PartialAgentConfig;
clientProperties: ClientProperties;
dataStore: DataStore;
loggers: Logger[];
}>;

export type ServerHealthState = ApiComponents["schemas"]["HealthState"];
Expand Down
4 changes: 2 additions & 2 deletions clients/tabby-agent/src/AnonymousUsageLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { v4 as uuid } from "uuid";
import type { paths as CloudApi } from "./types/cloudApi";
import { name as agentName, version as agentVersion } from "../package.json";
import { isBrowser } from "./env";
import { rootLogger } from "./logger";
import { logger } from "./logger";
import { DataStore } from "./dataStore";

export class AnonymousUsageLogger {
private anonymousUsageTrackingApi = createClient<CloudApi>({ baseUrl: "https://app.tabbyml.com/api" });
private logger = rootLogger.child({ component: "AnonymousUsage" });
private logger = logger("AnonymousUsage");
private systemData = {
agent: `${agentName}, ${agentVersion}`,
browser: isBrowser ? navigator?.userAgent || "browser" : undefined,
Expand Down
4 changes: 2 additions & 2 deletions clients/tabby-agent/src/Auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { paths as CloudApi } from "./types/cloudApi";
import type { AbortSignalOption } from "./Agent";
import { HttpError, abortSignalFromAnyOf } from "./utils";
import { DataStore, FileDataStore } from "./dataStore";
import { rootLogger } from "./logger";
import { logger } from "./logger";

export type StorageData = {
auth: { [endpoint: string]: { jwt: string } };
Expand Down Expand Up @@ -46,7 +46,7 @@ export class Auth extends EventEmitter {
},
};

private readonly logger = rootLogger.child({ component: "Auth" });
private readonly logger = logger("Auth");
private dataStore?: DataStore;
private authApi = createClient<CloudApi>({ baseUrl: "https://app.tabbyml.com/api" });
private jwt?: JWT;
Expand Down
4 changes: 2 additions & 2 deletions clients/tabby-agent/src/CompletionCache.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { LRUCache } from "lru-cache";
import { CompletionContext, CompletionResponse } from "./CompletionContext";
import { rootLogger } from "./logger";
import { logger } from "./logger";
import { splitLines, autoClosingPairs, findUnpairedAutoClosingChars } from "./utils";

type CompletionCacheKey = CompletionContext;
type CompletionCacheValue = CompletionResponse;

export class CompletionCache {
private readonly logger = rootLogger.child({ component: "CompletionCache" });
private readonly logger = logger("CompletionCache");
private options = {
maxCount: 10000,
prebuildCache: {
Expand Down
4 changes: 2 additions & 2 deletions clients/tabby-agent/src/JsonLineServer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import readline from "readline";
import { AgentFunction, AgentEvent, Agent, agentEventNames } from "./Agent";
import { rootLogger } from "./logger";
import { logger } from "./logger";
import { isCanceledError } from "./utils";

type AgentFunctionRequest<T extends keyof AgentFunction> = [
Expand Down Expand Up @@ -45,7 +45,7 @@ export class JsonLineServer {
private readonly process: NodeJS.Process = process;
private readonly inStream: NodeJS.ReadStream = process.stdin;
private readonly outStream: NodeJS.WriteStream = process.stdout;
private readonly logger = rootLogger.child({ component: "JsonLineServer" });
private readonly logger = logger("JsonLineServer");

private abortControllers: { [id: string]: AbortController } = {};

Expand Down
4 changes: 2 additions & 2 deletions clients/tabby-agent/src/LspServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import {
import { TextDocument } from "vscode-languageserver-textdocument";
import { name as agentName, version as agentVersion } from "../package.json";
import { Agent, StatusChangedEvent, CompletionRequest, CompletionResponse } from "./Agent";
import { rootLogger } from "./logger";
import { logger } from "./logger";
import { splitLines, isCanceledError } from "./utils";

export class LspServer {
private readonly connection = createConnection();
private readonly documents = new TextDocuments(TextDocument);

private readonly logger = rootLogger.child({ component: "LspServer" });
private readonly logger = logger("LspServer");

private agent?: Agent;

Expand Down
13 changes: 8 additions & 5 deletions clients/tabby-agent/src/TabbyAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ import { CompletionCache } from "./CompletionCache";
import { CompletionDebounce } from "./CompletionDebounce";
import { CompletionContext } from "./CompletionContext";
import { preCacheProcess, postCacheProcess } from "./postprocess";
import { rootLogger, allLoggers } from "./logger";
import { logger, allBasicLoggers, extraLogger } from "./logger";
import { AnonymousUsageLogger } from "./AnonymousUsageLogger";
import { CompletionProviderStats, CompletionProviderStatsEntry } from "./CompletionProviderStats";
import { loadTlsCaCerts } from "./loadCaCerts";

export class TabbyAgent extends EventEmitter implements Agent {
private readonly logger = rootLogger.child({ component: "TabbyAgent" });
private readonly logger = logger("TabbyAgent");
private anonymousUsageLogger = new AnonymousUsageLogger();
private config: AgentConfig = defaultAgentConfig;
private userConfig: PartialAgentConfig = {}; // config from `~/.tabby-client/agent/config.toml`
Expand Down Expand Up @@ -81,7 +81,7 @@ export class TabbyAgent extends EventEmitter implements Agent {
this.clientConfig,
this.serverProvidedConfig,
) as AgentConfig;
allLoggers.forEach((logger) => (logger.level = this.config.logs.level));
allBasicLoggers.forEach((logger) => (logger.level = this.config.logs.level));
this.anonymousUsageLogger.disabled = this.config.anonymousUsageTracking.disable;

await loadTlsCaCerts(this.config.tls);
Expand Down Expand Up @@ -343,6 +343,9 @@ export class TabbyAgent extends EventEmitter implements Agent {
}

public async initialize(options: AgentInitOptions): Promise<boolean> {
if (options.loggers) {
extraLogger.loggers = options.loggers;
}
this.dataStore = options.dataStore ?? defaultDataStore;
if (this.dataStore instanceof FileDataStore) {
try {
Expand All @@ -355,7 +358,7 @@ export class TabbyAgent extends EventEmitter implements Agent {
await this.anonymousUsageLogger.init({ dataStore: this.dataStore });
if (options.clientProperties) {
const { user: userProp, session: sessionProp } = options.clientProperties;
allLoggers.forEach((logger) => logger.setBindings?.({ ...sessionProp }));
allBasicLoggers.forEach((logger) => logger.setBindings?.({ ...sessionProp }));
if (sessionProp) {
Object.entries(sessionProp).forEach(([key, value]) => {
this.anonymousUsageLogger.setSessionProperties(key, value);
Expand Down Expand Up @@ -419,7 +422,7 @@ export class TabbyAgent extends EventEmitter implements Agent {
public async updateClientProperties(type: keyof ClientProperties, key: string, value: any): Promise<boolean> {
switch (type) {
case "session":
allLoggers.forEach((logger) => logger.setBindings?.(setProperty({}, key, value)));
allBasicLoggers.forEach((logger) => logger.setBindings?.(setProperty({}, key, value)));
this.anonymousUsageLogger.setSessionProperties(key, value);
break;
case "user":
Expand Down
4 changes: 2 additions & 2 deletions clients/tabby-agent/src/configFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import deepEqual from "deep-equal";
import { getProperty, deleteProperty } from "dot-prop";
import type { PartialAgentConfig } from "./AgentConfig";
import { isBrowser } from "./env";
import { rootLogger } from "./logger";
import { logger } from "./logger";

const configTomlTemplate = `## Tabby agent configuration file
Expand Down Expand Up @@ -82,7 +82,7 @@ function validateConfig(config: PartialAgentConfig): PartialAgentConfig {
class ConfigFile extends EventEmitter {
private data: PartialAgentConfig = {};
private watcher?: chokidar.FSWatcher;
private logger = rootLogger.child({ component: "ConfigFile" });
private logger = logger("ConfigFile");

constructor(private readonly filepath: string) {
super();
Expand Down
4 changes: 2 additions & 2 deletions clients/tabby-agent/src/loadCaCerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import * as macCa from "mac-ca";
import type { AgentConfig } from "./AgentConfig";
import { isBrowser } from "./env";
import "./ArrayExt";
import { rootLogger } from "./logger";
import { logger as getLogger } from "./logger";

type Cert = string | winCa.Certificate;

const logger = rootLogger.child({ component: "CaCert" });
const logger = getLogger("CaCert");
let extraCaCerts: Cert[] = [];
let originalCreateSecureContext: typeof tls.createSecureContext | undefined = undefined;

Expand Down
103 changes: 94 additions & 9 deletions clients/tabby-agent/src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,32 @@ import pino from "pino";
import * as FileStreamRotator from "file-stream-rotator";
import { isBrowser, isTest, testLogDebug } from "./env";

export interface LogFn {
(msg: string, ...args: any[]): void;
}

export interface Logger {
error: LogFn;
warn: LogFn;
info: LogFn;
debug: LogFn;
trace: LogFn;
}

export type ObjLogFn = {
<T extends object>(obj: T, msg?: string, ...args: any[]): void;
(obj: unknown, msg?: string, ...args: any[]): void;
(msg: string, ...args: any[]): void;
};

export interface ObjLogger {
error: ObjLogFn;
warn: ObjLogFn;
info: ObjLogFn;
debug: ObjLogFn;
trace: ObjLogFn;
}

class LogFileStream implements pino.DestinationStream {
private streamOptions = {
// Rotating file locate at `~/.tabby-client/agent/logs/`.
Expand All @@ -25,17 +51,76 @@ class LogFileStream implements pino.DestinationStream {
}

// LogFileStream not available in browser, will use default browser console output instead.
const stream = isBrowser || isTest ? undefined : new LogFileStream();

const options = { serializers: { error: pino.stdSerializers.err } };
export const rootLogger = stream ? pino(options, stream) : pino(options);
const logFileStream = isBrowser || isTest ? undefined : new LogFileStream();
const pinoOptions = { serializers: { error: pino.stdSerializers.err } };
const rootBasicLogger = logFileStream ? pino(pinoOptions, logFileStream) : pino(pinoOptions);
if (isTest && testLogDebug) {
rootLogger.level = "debug";
rootBasicLogger.level = "debug";
} else {
rootLogger.level = "silent";
rootBasicLogger.level = "silent";
}
export const allBasicLoggers = [rootBasicLogger];
rootBasicLogger.onChild = (child: pino.Logger) => {
allBasicLoggers.push(child);
};

export const allLoggers = [rootLogger];
rootLogger.onChild = (child: pino.Logger) => {
allLoggers.push(child);
function toObjLogFn(logFn: LogFn): ObjLogFn {
return (...args: any[]) => {
const arg0 = args.shift();
if (typeof arg0 === "string") {
logFn(arg0, ...args);
} else {
const arg1 = args.shift();
if (typeof arg1 === "string") {
logFn(arg1, ...args, arg0);
} else {
logFn(arg0, arg1, ...args);
}
}
};
}

function withComponent(logFn: LogFn, component: string): LogFn {
return (msg: string, ...args: any[]) => {
logFn(`[${component}] ${msg ?? ""}`, ...args);
};
}

export const extraLogger = {
loggers: [] as Logger[],
child(component: string): ObjLogger {
const buildLogFn = (level: keyof Logger) => {
const logFn = (...args: any[]) => {
const arg0 = args.shift();
this.loggers.forEach((logger) => logger[level](arg0, ...args));
};
return toObjLogFn(withComponent(logFn, component));
};
return {
error: buildLogFn("error"),
warn: buildLogFn("warn"),
info: buildLogFn("info"),
debug: buildLogFn("debug"),
trace: buildLogFn("trace"),
};
},
};

export function logger(component: string): ObjLogger {
const basic = rootBasicLogger.child({ component });
const extra = extraLogger.child(component);
const all = [basic, extra];
const buildLogFn = (level: keyof ObjLogger) => {
return (...args: any[]) => {
const arg0 = args.shift();
all.forEach((logger) => logger[level](arg0, ...args));
};
};
return {
error: buildLogFn("error"),
warn: buildLogFn("warn"),
info: buildLogFn("info"),
debug: buildLogFn("debug"),
trace: buildLogFn("trace"),
};
}
4 changes: 2 additions & 2 deletions clients/tabby-agent/src/postprocess/base.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CompletionResponse, CompletionResponseChoice, CompletionContext } from "../CompletionContext";
import { rootLogger } from "../logger";
import { logger as getLogger } from "../logger";
import "../ArrayExt";

type PostprocessFilterBase<T extends string | CompletionResponseChoice> = (
Expand All @@ -10,7 +10,7 @@ type PostprocessFilterBase<T extends string | CompletionResponseChoice> = (
export type PostprocessFilter = PostprocessFilterBase<string>;
export type PostprocessChoiceFilter = PostprocessFilterBase<CompletionResponseChoice>;

export const logger = rootLogger.child({ component: "Postprocess" });
export const logger = getLogger("Postprocess");

export function applyFilter(
filter: PostprocessFilter,
Expand Down
Loading

0 comments on commit 2b7a303

Please sign in to comment.