diff --git a/web/packages/teleterm/src/services/logger/loggerService.ts b/web/packages/teleterm/src/services/logger/loggerService.ts index 0b833c67b2ae5..de88c9b75c38d 100644 --- a/web/packages/teleterm/src/services/logger/loggerService.ts +++ b/web/packages/teleterm/src/services/logger/loggerService.ts @@ -30,6 +30,8 @@ import split2 from 'split2'; import { Logger, LoggerService, NodeLoggerService } from './types'; import { KeepLastChunks } from './keepLastChunks'; +import type { Logform } from 'winston'; + import type { ChildProcess } from 'node:child_process'; /** @@ -95,22 +97,12 @@ export function createFileLoggerService( }); if (opts.dev) { - instance.add( - new transports.Console({ - format: format.printf(({ level, message, context }) => { - const loggerName = - opts.loggerNameColor && - `\x1b[${opts.loggerNameColor}m${opts.name.toUpperCase()}\x1b[0m`; - - const text = stringifier(message as unknown as unknown[]); - const logMessage = opts.passThroughMode - ? text - : `[${context}] ${level}: ${text}`; - - return [loggerName, logMessage].filter(Boolean).join(' '); - }), - }) - ); + // Browser environment. + if (typeof window !== 'undefined') { + instance.add(getBrowserConsoleTransport(opts)); + } else { + instance.add(getRegularConsoleTransport(opts)); + } } return { @@ -217,3 +209,46 @@ type FileLoggerOptions = { * */ omitTimestamp?: boolean; }; + +/** Does not stringify messages and logs directly using `console.*` functions. */ +function getBrowserConsoleTransport(opts: FileLoggerOptions) { + return new transports.Console({ + log({ level, message, context }: Logform.TransformableInfo, next) { + const loggerName = getLoggerName(opts); + + const logMessage = opts.passThroughMode + ? message + : [`[${context}] ${level}:`, ...message]; + + const toLog = [loggerName, logMessage].filter(Boolean).flat(); + // We allow level to be only info, warn and error (createLoggerFromWinston). + console[level](...toLog); + next(); + }, + }); +} + +/** Stringifies log messages and logs with winston's console transport. */ +function getRegularConsoleTransport(opts: FileLoggerOptions) { + return new transports.Console({ + format: format.printf(({ level, message, context }) => { + const loggerName = getLoggerName(opts); + + const text = stringifier(message as unknown as unknown[]); + const logMessage = opts.passThroughMode + ? text + : `[${context}] ${level}: ${text}`; + + return [loggerName, logMessage].filter(Boolean).join(' '); + }), + }); +} + +function getLoggerName( + opts: Pick +) { + return ( + opts.loggerNameColor && + `\x1b[${opts.loggerNameColor}m${opts.name.toUpperCase()}\x1b[0m` + ); +} diff --git a/web/packages/teleterm/src/services/tshd/interceptors.test.ts b/web/packages/teleterm/src/services/tshd/interceptors.test.ts index 52d7e0b9adfb8..917906a8184af 100644 --- a/web/packages/teleterm/src/services/tshd/interceptors.test.ts +++ b/web/packages/teleterm/src/services/tshd/interceptors.test.ts @@ -40,7 +40,7 @@ it('do not log sensitive info like password', () => { service: { typeName: 'FooService' } as ServiceInfo, } as MethodInfo, { - passw: {}, + password: {}, userData: { login: 'admin', password: 'admin', @@ -50,7 +50,7 @@ it('do not log sensitive info like password', () => { ); expect(infoLogger).toHaveBeenCalledWith(expect.any(String), { - passw: '~FILTERED~', + password: '~FILTERED~', userData: { login: 'admin', password: '~FILTERED~' }, }); }); diff --git a/web/packages/teleterm/src/services/tshd/interceptors.ts b/web/packages/teleterm/src/services/tshd/interceptors.ts index 3608039462fb9..a3375ea1f32a2 100644 --- a/web/packages/teleterm/src/services/tshd/interceptors.ts +++ b/web/packages/teleterm/src/services/tshd/interceptors.ts @@ -22,7 +22,7 @@ import { isObject } from 'shared/utils/highbar'; import Logger from 'teleterm/logger'; -const SENSITIVE_PROPERTIES = ['passw', 'authClusterId', 'pin']; +const SENSITIVE_PROPERTIES = ['password', 'authClusterId', 'pin']; export function loggingInterceptor(logger: Logger): RpcInterceptor { return { @@ -109,7 +109,7 @@ export function filterSensitiveProperties(toFilter: object): object { const transformer = (result: object, value: any, key: any) => { if ( SENSITIVE_PROPERTIES.some( - sensitiveProp => typeof key === 'string' && key.includes(sensitiveProp) + sensitiveProp => typeof key === 'string' && key === sensitiveProp ) ) { result[key] = '~FILTERED~';