add logging in sever side
This commit is contained in:
parent
8a519d8dc4
commit
89e404adf3
|
@ -0,0 +1,183 @@
|
||||||
|
/**
|
||||||
|
* Logging utilities
|
||||||
|
* copy from vscode-cmake-tools
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as node_fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
/** Logging levels */
|
||||||
|
export enum LogLevel {
|
||||||
|
Debug,
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error,
|
||||||
|
Off,
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogLevelKey = 'debug' | 'info' | 'warning' | 'error' | 'off';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of a logging level
|
||||||
|
* @param level A logging level
|
||||||
|
*/
|
||||||
|
function levelName(level: LogLevel): LogLevelKey {
|
||||||
|
switch (level) {
|
||||||
|
case LogLevel.Debug:
|
||||||
|
return 'debug';
|
||||||
|
case LogLevel.Info:
|
||||||
|
return 'info';
|
||||||
|
case LogLevel.Warning:
|
||||||
|
return 'warning';
|
||||||
|
case LogLevel.Error:
|
||||||
|
return 'error';
|
||||||
|
case LogLevel.Off:
|
||||||
|
return 'off';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if logging is enabled for the given LogLevel
|
||||||
|
* @param level The log level to check
|
||||||
|
*/
|
||||||
|
function levelEnabled(level: LogLevel): boolean {
|
||||||
|
// const strlevel = vscode.workspace.getConfiguration('cmake').get<LogLevelKey>('loggingLevel', 'info');
|
||||||
|
const strLevel = extSettings.loggingLevel;
|
||||||
|
switch (strLevel) {
|
||||||
|
case 'debug':
|
||||||
|
return level >= LogLevel.Debug;
|
||||||
|
case 'info':
|
||||||
|
return level >= LogLevel.Info;
|
||||||
|
case 'warning':
|
||||||
|
return level >= LogLevel.Warning;
|
||||||
|
case 'error':
|
||||||
|
return level >= LogLevel.Error;
|
||||||
|
case 'off':
|
||||||
|
return level >= LogLevel.Off;
|
||||||
|
default:
|
||||||
|
console.error('Invalid logging level in settings.json');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Stringable {
|
||||||
|
toString(): string;
|
||||||
|
toLocaleString(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _LOGGER: NodeJS.WritableStream;
|
||||||
|
|
||||||
|
export function logFilePath(): string {
|
||||||
|
return path.join(paths.dataDir, 'log.txt');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _openLogFile() {
|
||||||
|
if (!_LOGGER) {
|
||||||
|
const fpath = logFilePath();
|
||||||
|
await mkdir_p(path.dirname(fpath));
|
||||||
|
if (await exists(fpath)) {
|
||||||
|
_LOGGER = node_fs.createWriteStream(fpath, { flags: 'r+' });
|
||||||
|
} else {
|
||||||
|
_LOGGER = node_fs.createWriteStream(fpath, { flags: 'w' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _LOGGER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages and controls logging
|
||||||
|
*/
|
||||||
|
class SingletonLogger {
|
||||||
|
private readonly _logStream = _openLogFile();
|
||||||
|
|
||||||
|
private _log(level: LogLevel, ...args: Stringable[]) {
|
||||||
|
|
||||||
|
if (!levelEnabled(level)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user_message = args.map(a => a.toString()).join(' ');
|
||||||
|
const prefix = new Date().toISOString() + ` [${levelName(level)}]`;
|
||||||
|
const raw_message = `${prefix} ${user_message}`;
|
||||||
|
switch (level) {
|
||||||
|
// case LogLevel.Trace:
|
||||||
|
case LogLevel.Debug:
|
||||||
|
case LogLevel.Info:
|
||||||
|
// case LogLevel.Note:
|
||||||
|
if (process.env['CMT_QUIET_CONSOLE'] !== '1') {
|
||||||
|
console.info('[cmake-intellisence]', raw_message);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LogLevel.Warning:
|
||||||
|
console.warn('[cmake-intellisence]', raw_message);
|
||||||
|
break;
|
||||||
|
case LogLevel.Error:
|
||||||
|
console.error('[cmake-intellisence]', raw_message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Write to the logfile asynchronously.
|
||||||
|
this._logStream.then(strm => strm.write(raw_message + '\n')).catch(e => {
|
||||||
|
console.error('Unhandled error while writing cmake-intellisence log file', e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(...args: Stringable[]) {
|
||||||
|
this._log(LogLevel.Debug, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
info(...args: Stringable[]) {
|
||||||
|
this._log(LogLevel.Info, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
warning(...args: Stringable[]) {
|
||||||
|
this._log(LogLevel.Warning, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(...args: Stringable[]) {
|
||||||
|
this._log(LogLevel.Error, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _inst: SingletonLogger | null = null;
|
||||||
|
|
||||||
|
static instance(): SingletonLogger {
|
||||||
|
if (SingletonLogger._inst === null) {
|
||||||
|
SingletonLogger._inst = new SingletonLogger();
|
||||||
|
}
|
||||||
|
return SingletonLogger._inst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Logger {
|
||||||
|
constructor(readonly _tag: string) { }
|
||||||
|
get tag() {
|
||||||
|
return `[${this._tag}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(...args: Stringable[]) {
|
||||||
|
SingletonLogger.instance().debug(this.tag, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
info(...args: Stringable[]) {
|
||||||
|
SingletonLogger.instance().info(this.tag, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
warning(...args: Stringable[]) {
|
||||||
|
SingletonLogger.instance().warning(this.tag, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(...args: Stringable[]) {
|
||||||
|
SingletonLogger.instance().error(this.tag, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static logTestName(suite?: string, test?: string) {
|
||||||
|
SingletonLogger.instance().info('-----------------------------------------------------------------------');
|
||||||
|
SingletonLogger.instance().info(`Beginning test: ${suite ?? 'unknown suite'} - ${test ?? 'unknown test'}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createLogger(tag: string) {
|
||||||
|
return new Logger(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
import paths, { mkdir_p, exists } from './paths';
|
||||||
|
import { extSettings } from './settings';
|
|
@ -0,0 +1,191 @@
|
||||||
|
/**
|
||||||
|
* This module defines important directories and paths to the extension
|
||||||
|
* copy from vscode-cmake-tools
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as util from 'util';
|
||||||
|
|
||||||
|
const promisify = util.promisify;
|
||||||
|
export const stat = promisify(fs.stat);
|
||||||
|
export const mkdir = promisify(fs.mkdir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try and stat() a file/folder. If stat() fails for *any reason*, returns `null`.
|
||||||
|
* @param filePath The file to try and stat()
|
||||||
|
*/
|
||||||
|
export async function tryStat(filePath: fs.PathLike): Promise<fs.Stats | null> {
|
||||||
|
try {
|
||||||
|
return await stat(filePath);
|
||||||
|
} catch (_e) {
|
||||||
|
// Don't even bother with the error. Any number of things might have gone
|
||||||
|
// wrong. Probably one of: Non-existing file, bad permissions, bad path.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function exists(filePath: string): Promise<boolean> {
|
||||||
|
const stat = await tryStat(filePath);
|
||||||
|
return stat !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a directory and all parent directories recursively. If the file
|
||||||
|
* already exists, and is not a directory, just return.
|
||||||
|
* @param fspath The directory to create
|
||||||
|
*/
|
||||||
|
export async function mkdir_p(fspath: string): Promise<void> {
|
||||||
|
const parent = path.dirname(fspath);
|
||||||
|
if (!await exists(parent)) {
|
||||||
|
await mkdir_p(parent);
|
||||||
|
} else {
|
||||||
|
if (!(await stat(parent)).isDirectory()) {
|
||||||
|
throw new Error(`cannot.create.path', 'Cannot create ${fspath}: ${fspath} is a non-directory`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!await exists(fspath)) {
|
||||||
|
await mkdir(fspath);
|
||||||
|
} else {
|
||||||
|
if (!(await stat(fspath)).isDirectory()) {
|
||||||
|
throw new Error(`cannot.create.directory', 'Cannot create directory ${fspath}. It exists, and is not a directory!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WindowsEnvironment {
|
||||||
|
get AppData(): string | undefined {
|
||||||
|
return process.env['APPDATA'];
|
||||||
|
}
|
||||||
|
|
||||||
|
get LocalAppData(): string | undefined {
|
||||||
|
return process.env['LOCALAPPDATA'];
|
||||||
|
}
|
||||||
|
|
||||||
|
get AllUserProfile(): string | undefined {
|
||||||
|
return process.env['ProgramData'];
|
||||||
|
}
|
||||||
|
|
||||||
|
get ComSpec(): string {
|
||||||
|
let comSpec = process.env['ComSpec'];
|
||||||
|
|
||||||
|
if (undefined === comSpec) {
|
||||||
|
comSpec = this.SystemRoot! + '\\system32\\cmd.exe';
|
||||||
|
}
|
||||||
|
|
||||||
|
return comSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
get HomeDrive(): string | undefined {
|
||||||
|
return process.env['HOMEDRIVE'];
|
||||||
|
}
|
||||||
|
|
||||||
|
get HomePath(): string | undefined {
|
||||||
|
return process.env['HOMEPATH'];
|
||||||
|
}
|
||||||
|
|
||||||
|
get ProgramFilesX86(): string | undefined {
|
||||||
|
return process.env['ProgramFiles(x86)'];
|
||||||
|
}
|
||||||
|
|
||||||
|
get ProgramFiles(): string | undefined {
|
||||||
|
return process.env['ProgramFiles'];
|
||||||
|
}
|
||||||
|
|
||||||
|
get SystemDrive(): string | undefined {
|
||||||
|
return process.env['SystemDrive'];
|
||||||
|
}
|
||||||
|
|
||||||
|
get SystemRoot(): string | undefined {
|
||||||
|
return process.env['SystemRoot'];
|
||||||
|
}
|
||||||
|
|
||||||
|
get Temp(): string | undefined {
|
||||||
|
return process.env['TEMP'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory class.
|
||||||
|
*/
|
||||||
|
class Paths {
|
||||||
|
private _ninjaPath?: string;
|
||||||
|
|
||||||
|
readonly windows: WindowsEnvironment = new WindowsEnvironment();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current user's home directory
|
||||||
|
*/
|
||||||
|
get userHome(): string {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
return path.join(process.env['HOMEDRIVE'] || 'C:', process.env['HOMEPATH'] || 'Users\\Public');
|
||||||
|
} else {
|
||||||
|
return process.env['HOME'] || process.env['PROFILE']!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user-local data directory. This is where user-specific persistent
|
||||||
|
* application data should be stored.
|
||||||
|
*/
|
||||||
|
get userLocalDir(): string {
|
||||||
|
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
return this.windows.LocalAppData!;
|
||||||
|
} else {
|
||||||
|
const xdg_dir = process.env['XDG_DATA_HOME'];
|
||||||
|
if (xdg_dir) {
|
||||||
|
return xdg_dir;
|
||||||
|
}
|
||||||
|
const home = this.userHome;
|
||||||
|
return path.join(home, '.local/share');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get userRoamingDir(): string {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
return this.windows.AppData!;
|
||||||
|
} else {
|
||||||
|
const xdg_dir = process.env['XDG_CONFIG_HOME'];
|
||||||
|
if (xdg_dir) {
|
||||||
|
return xdg_dir;
|
||||||
|
}
|
||||||
|
const home = this.userHome;
|
||||||
|
return path.join(home, '.config');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The directory where CMake Tools should store user-specific persistent
|
||||||
|
* data.
|
||||||
|
*/
|
||||||
|
get dataDir(): string {
|
||||||
|
return path.join(this.userLocalDir, 'cmake-intellisence');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "roaming" directory where CMake Tools stores roaming configuration
|
||||||
|
* data.
|
||||||
|
*/
|
||||||
|
get roamingDataDir(): string {
|
||||||
|
return path.join(this.userRoamingDir, 'cmake-intellisence');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the platform-specific temporary directory
|
||||||
|
*/
|
||||||
|
get tmpDir(): string {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
return this.windows.Temp!;
|
||||||
|
} else {
|
||||||
|
return '/tmp';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get ninjaPath() {
|
||||||
|
return this._ninjaPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const paths = new Paths();
|
||||||
|
export default paths;
|
|
@ -29,6 +29,7 @@ import { getTokenBuilder, getTokenModifiers, getTokenTypes, SemanticListener, to
|
||||||
import { extSettings } from './settings';
|
import { extSettings } from './settings';
|
||||||
import { DefinationListener, incToBaseDir, refToDef, topScope } from './symbolTable/goToDefination';
|
import { DefinationListener, incToBaseDir, refToDef, topScope } from './symbolTable/goToDefination';
|
||||||
import { getFileContext } from './utils';
|
import { getFileContext } from './utils';
|
||||||
|
import { createLogger } from './logging';
|
||||||
|
|
||||||
type Word = {
|
type Word = {
|
||||||
text: string,
|
text: string,
|
||||||
|
@ -47,6 +48,7 @@ export const connection = createConnection(ProposedFeatures.all);
|
||||||
// Create a simple text document manager.
|
// Create a simple text document manager.
|
||||||
export const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
|
export const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
|
||||||
|
|
||||||
|
export const logger = createLogger('server');
|
||||||
|
|
||||||
connection.onInitialize(async (params: InitializeParams) => {
|
connection.onInitialize(async (params: InitializeParams) => {
|
||||||
initParams = params;
|
initParams = params;
|
||||||
|
@ -312,6 +314,7 @@ connection.onDefinition((params: DefinitionParams) => {
|
||||||
if (refToDef.has(wordPos)) {
|
if (refToDef.has(wordPos)) {
|
||||||
resolve(refToDef.get(wordPos));
|
resolve(refToDef.get(wordPos));
|
||||||
} else {
|
} else {
|
||||||
|
logger.warning(`can't find defination, word: ${word.text}, wordPos: ${wordPos}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -481,6 +484,7 @@ function getProposals(word: string, kind: CompletionItemKind, dataSource: string
|
||||||
// Make the text document manager listen on the connection
|
// Make the text document manager listen on the connection
|
||||||
// for open, change and close text document events
|
// for open, change and close text document events
|
||||||
documents.listen(connection);
|
documents.listen(connection);
|
||||||
|
logger.info('listen on connection');
|
||||||
|
|
||||||
// Listen on the connection
|
// Listen on the connection
|
||||||
connection.listen();
|
connection.listen();
|
||||||
|
|
|
@ -7,7 +7,7 @@ import antlr4 from './parser/antlr4/index.js';
|
||||||
import InputStream from './parser/antlr4/InputStream';
|
import InputStream from './parser/antlr4/InputStream';
|
||||||
import CMakeLexer from "./parser/CMakeLexer";
|
import CMakeLexer from "./parser/CMakeLexer";
|
||||||
import CMakeParser from "./parser/CMakeParser";
|
import CMakeParser from "./parser/CMakeParser";
|
||||||
import { documents } from './server';
|
import { documents, logger } from './server';
|
||||||
|
|
||||||
export function getFileContext(uri: URI) {
|
export function getFileContext(uri: URI) {
|
||||||
const document = documents.get(uri.toString());
|
const document = documents.get(uri.toString());
|
||||||
|
@ -28,6 +28,8 @@ export function getSubCMakeListsUri(baseDir: URI, subDir: string): URI {
|
||||||
const subCMakeListsUri: URI = Utils.joinPath(baseDir, subDir, 'CMakeLists.txt');
|
const subCMakeListsUri: URI = Utils.joinPath(baseDir, subDir, 'CMakeLists.txt');
|
||||||
if (existsSync(subCMakeListsUri.fsPath)) {
|
if (existsSync(subCMakeListsUri.fsPath)) {
|
||||||
return subCMakeListsUri;
|
return subCMakeListsUri;
|
||||||
|
} else {
|
||||||
|
logger.error('getSubCMakeListsUri:', subCMakeListsUri.fsPath, 'not exist');
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -37,6 +39,8 @@ export function getIncludeFileUri(baseDir: URI, includeFileName: string): URI {
|
||||||
const incFileUri: URI = Utils.joinPath(baseDir, includeFileName);
|
const incFileUri: URI = Utils.joinPath(baseDir, includeFileName);
|
||||||
if (existsSync(incFileUri.fsPath)) {
|
if (existsSync(incFileUri.fsPath)) {
|
||||||
return incFileUri;
|
return incFileUri;
|
||||||
|
} else {
|
||||||
|
logger.error('getIncludeFileUri:', incFileUri.fsPath, 'not exist');
|
||||||
}
|
}
|
||||||
|
|
||||||
const cmakePath: string = which('cmake');
|
const cmakePath: string = which('cmake');
|
||||||
|
@ -50,6 +54,8 @@ export function getIncludeFileUri(baseDir: URI, includeFileName: string): URI {
|
||||||
if (existsSync(resPath)) {
|
if (existsSync(resPath)) {
|
||||||
// return pathToFileURL(resPath).toString();
|
// return pathToFileURL(resPath).toString();
|
||||||
return URI.file(resPath);
|
return URI.file(resPath);
|
||||||
|
} else {
|
||||||
|
logger.error('getIncludeFileUri:', resPath, 'not exist');
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
Loading…
Reference in New Issue