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 { DefinationListener, incToBaseDir, refToDef, topScope } from './symbolTable/goToDefination';
|
||||
import { getFileContext } from './utils';
|
||||
import { createLogger } from './logging';
|
||||
|
||||
type Word = {
|
||||
text: string,
|
||||
|
@ -47,6 +48,7 @@ export const connection = createConnection(ProposedFeatures.all);
|
|||
// Create a simple text document manager.
|
||||
export const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
|
||||
|
||||
export const logger = createLogger('server');
|
||||
|
||||
connection.onInitialize(async (params: InitializeParams) => {
|
||||
initParams = params;
|
||||
|
@ -312,6 +314,7 @@ connection.onDefinition((params: DefinitionParams) => {
|
|||
if (refToDef.has(wordPos)) {
|
||||
resolve(refToDef.get(wordPos));
|
||||
} else {
|
||||
logger.warning(`can't find defination, word: ${word.text}, wordPos: ${wordPos}`);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
@ -481,6 +484,7 @@ function getProposals(word: string, kind: CompletionItemKind, dataSource: string
|
|||
// Make the text document manager listen on the connection
|
||||
// for open, change and close text document events
|
||||
documents.listen(connection);
|
||||
logger.info('listen on connection');
|
||||
|
||||
// Listen on the connection
|
||||
connection.listen();
|
||||
|
|
|
@ -7,7 +7,7 @@ import antlr4 from './parser/antlr4/index.js';
|
|||
import InputStream from './parser/antlr4/InputStream';
|
||||
import CMakeLexer from "./parser/CMakeLexer";
|
||||
import CMakeParser from "./parser/CMakeParser";
|
||||
import { documents } from './server';
|
||||
import { documents, logger } from './server';
|
||||
|
||||
export function getFileContext(uri: URI) {
|
||||
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');
|
||||
if (existsSync(subCMakeListsUri.fsPath)) {
|
||||
return subCMakeListsUri;
|
||||
} else {
|
||||
logger.error('getSubCMakeListsUri:', subCMakeListsUri.fsPath, 'not exist');
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -37,6 +39,8 @@ export function getIncludeFileUri(baseDir: URI, includeFileName: string): URI {
|
|||
const incFileUri: URI = Utils.joinPath(baseDir, includeFileName);
|
||||
if (existsSync(incFileUri.fsPath)) {
|
||||
return incFileUri;
|
||||
} else {
|
||||
logger.error('getIncludeFileUri:', incFileUri.fsPath, 'not exist');
|
||||
}
|
||||
|
||||
const cmakePath: string = which('cmake');
|
||||
|
@ -50,6 +54,8 @@ export function getIncludeFileUri(baseDir: URI, includeFileName: string): URI {
|
|||
if (existsSync(resPath)) {
|
||||
// return pathToFileURL(resPath).toString();
|
||||
return URI.file(resPath);
|
||||
} else {
|
||||
logger.error('getIncludeFileUri:', resPath, 'not exist');
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
Loading…
Reference in New Issue