mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-02 09:35:40 -05:00
* Adding support for configuring SqlTools log levels from user configuration. This also adds changes to see the tail of the sqltoolsservicelayer log file in the newly created 'Output->Log (SqlTools)' channel * Three new user settings control how logging happens. tracingLevel, logRetentionMinutes & logFilesRemovalLimit. Default tracingLevel is set to 'Critical'. * The logfiles include ui Extension host process id in their log file names. This ensures that filenames from multiple instances of Azure Data Studio running do not collide with each other. Furthermore log directory for being used for the tools service backend processes. This ensures that there is no name conflict when multiple instances of azuredatastudio are running on the same box. Also when azuredatastudio is started from vscode under debugger the log directory is set to %APPDATA%\Code\mssql while the official location is %APPDATA%\azuredatastudio\mssql. So dev environment should not affect other running instances. Kindly note that all debug runs of azuredatastudio share the same directory and all non debug runs share a directory different from those running under debugger. * Log files older than a week get cleaned up upon start-up. The log file cleanup behavior can be controlled at user level by logRetentionMinutes & logFilesRemovalLimit settings.
172 lines
5.3 KiB
TypeScript
172 lines
5.3 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
'use strict';
|
|
|
|
import * as path from 'path';
|
|
import * as crypto from 'crypto';
|
|
import * as os from 'os';
|
|
import {workspace, WorkspaceConfiguration} from 'vscode';
|
|
import * as findRemoveSync from 'find-remove';
|
|
|
|
const configTracingLevel = 'tracingLevel';
|
|
const configLogRetentionMinutes = 'logRetentionMinutes';
|
|
const configLogFilesRemovalLimit = 'logFilesRemovalLimit';
|
|
const extensionConfigSectionName = 'mssql';
|
|
|
|
// The function is a duplicate of \src\paths.js. IT would be better to import path.js but it doesn't
|
|
// work for now because the extension is running in different process.
|
|
export function getAppDataPath() {
|
|
let platform = process.platform;
|
|
switch (platform) {
|
|
case 'win32': return process.env['APPDATA'] || path.join(process.env['USERPROFILE'], 'AppData', 'Roaming');
|
|
case 'darwin': return path.join(os.homedir(), 'Library', 'Application Support');
|
|
case 'linux': return process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config');
|
|
default: throw new Error('Platform not supported');
|
|
}
|
|
}
|
|
|
|
export function removeOldLogFiles(prefix: string) : JSON {
|
|
return findRemoveSync(getDefaultLogDir(), {prefix: `${prefix}_`, age: {seconds: getConfigLogRetentionSeconds()}, limit: getConfigLogFilesRemovalLimit()});
|
|
}
|
|
|
|
export function getConfiguration(config: string = extensionConfigSectionName) : WorkspaceConfiguration {
|
|
return workspace.getConfiguration(extensionConfigSectionName);
|
|
}
|
|
|
|
export function getConfigLogFilesRemovalLimit() : number {
|
|
let config = getConfiguration();
|
|
if (config) {
|
|
return Number((config[configLogFilesRemovalLimit]).toFixed(0));
|
|
}
|
|
else
|
|
{
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export function getConfigLogRetentionSeconds() : number {
|
|
let config = getConfiguration();
|
|
if (config) {
|
|
return Number((config[configLogRetentionMinutes] * 60).toFixed(0));
|
|
}
|
|
else
|
|
{
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export function getConfigTracingLevel() : string {
|
|
let config = getConfiguration();
|
|
if (config) {
|
|
return config[configTracingLevel];
|
|
}
|
|
else
|
|
{
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export function getDefaultLogDir() : string {
|
|
return path.join(process.env['VSCODE_LOGS'], '..', '..','mssql');
|
|
}
|
|
|
|
export function getDefaultLogFile(prefix: string, pid: number) : string {
|
|
return path.join(getDefaultLogDir(), `${prefix}_${pid}.log`);
|
|
}
|
|
|
|
export function getCommonLaunchArgsAndCleanupOldLogFiles(prefix: string, executablePath: string) : string [] {
|
|
let launchArgs = [];
|
|
launchArgs.push('--log-file');
|
|
let logFile = getDefaultLogFile(prefix, process.pid);
|
|
launchArgs.push(logFile);
|
|
|
|
console.log(`logFile for ${path.basename(executablePath)} is ${logFile}`);
|
|
console.log(`This process (ui Extenstion Host) is pid: ${process.pid}`);
|
|
// Delete old log files
|
|
let deletedLogFiles = removeOldLogFiles(prefix);
|
|
console.log(`Old log files deletion report: ${JSON.stringify(deletedLogFiles)}`);
|
|
launchArgs.push('--tracing-level');
|
|
launchArgs.push(getConfigTracingLevel());
|
|
return launchArgs;
|
|
}
|
|
|
|
export function ensure(target: object, key: string): any {
|
|
if (target[key] === void 0) {
|
|
target[key] = {} as any;
|
|
}
|
|
return target[key];
|
|
}
|
|
|
|
export interface IPackageInfo {
|
|
name: string;
|
|
version: string;
|
|
aiKey: string;
|
|
}
|
|
|
|
export function getPackageInfo(packageJson: any): IPackageInfo {
|
|
if (packageJson) {
|
|
return {
|
|
name: packageJson.name,
|
|
version: packageJson.version,
|
|
aiKey: packageJson.aiKey
|
|
};
|
|
}
|
|
}
|
|
|
|
export function generateUserId(): Promise<string> {
|
|
return new Promise<string>(resolve => {
|
|
try {
|
|
let interfaces = os.networkInterfaces();
|
|
let mac;
|
|
for (let key of Object.keys(interfaces)) {
|
|
let item = interfaces[key][0];
|
|
if (!item.internal) {
|
|
mac = item.mac;
|
|
break;
|
|
}
|
|
}
|
|
if (mac) {
|
|
resolve(crypto.createHash('sha256').update(mac + os.homedir(), 'utf8').digest('hex'));
|
|
} else {
|
|
resolve(generateGuid());
|
|
}
|
|
} catch (err) {
|
|
resolve(generateGuid()); // fallback
|
|
}
|
|
});
|
|
}
|
|
|
|
export function generateGuid(): string {
|
|
let hexValues: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
|
|
// c.f. rfc4122 (UUID version 4 = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
|
|
let oct: string = '';
|
|
let tmp: number;
|
|
/* tslint:disable:no-bitwise */
|
|
for (let a: number = 0; a < 4; a++) {
|
|
tmp = (4294967296 * Math.random()) | 0;
|
|
oct += hexValues[tmp & 0xF] +
|
|
hexValues[tmp >> 4 & 0xF] +
|
|
hexValues[tmp >> 8 & 0xF] +
|
|
hexValues[tmp >> 12 & 0xF] +
|
|
hexValues[tmp >> 16 & 0xF] +
|
|
hexValues[tmp >> 20 & 0xF] +
|
|
hexValues[tmp >> 24 & 0xF] +
|
|
hexValues[tmp >> 28 & 0xF];
|
|
}
|
|
|
|
// 'Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively'
|
|
let clockSequenceHi: string = hexValues[8 + (Math.random() * 4) | 0];
|
|
return oct.substr(0, 8) + '-' + oct.substr(9, 4) + '-4' + oct.substr(13, 3) + '-' + clockSequenceHi + oct.substr(16, 3) + '-' + oct.substr(19, 12);
|
|
/* tslint:enable:no-bitwise */
|
|
}
|
|
|
|
export function verifyPlatform(): Thenable<boolean> {
|
|
if (os.platform() === 'darwin' && parseFloat(os.release()) < 16.0) {
|
|
return Promise.resolve(false);
|
|
} else {
|
|
return Promise.resolve(true);
|
|
}
|
|
}
|