add logging in sever side

This commit is contained in:
全卓 2022-12-02 16:23:39 +08:00
parent 8a519d8dc4
commit 89e404adf3
4 changed files with 385 additions and 1 deletions

183
server/src/logging.ts Normal file
View File

@ -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';

191
server/src/paths.ts Normal file
View File

@ -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;

View File

@ -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();

View File

@ -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;