diff --git a/src/mol-model/structure/structure/symmetry.ts b/src/mol-model/structure/structure/symmetry.ts index 0a912a4ab1ab61f8d08cb533ca052e634a81e538..ccbfce2aec1b61b744ec29500d3120d3e2c23381 100644 --- a/src/mol-model/structure/structure/symmetry.ts +++ b/src/mol-model/structure/structure/symmetry.ts @@ -43,7 +43,10 @@ namespace StructureSymmetry { }); } - // TODO: build symmetry mates within radius + export function builderSymmetryMates(structure: Structure, radius: number) { + // TODO: do it properly + return buildSymmetryRange(structure, Vec3.create(-3, -3, -3), Vec3.create(3, 3, 3)); + } export function buildSymmetryRange(structure: Structure, ijkMin: Vec3, ijkMax: Vec3) { return Task.create('Build Assembly', async ctx => { diff --git a/src/mol-util/console-logger.ts b/src/mol-util/console-logger.ts new file mode 100644 index 0000000000000000000000000000000000000000..6945b7c04e5a01d079d5d44d4daff16201367a51 --- /dev/null +++ b/src/mol-util/console-logger.ts @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +export namespace ConsoleLogger { + export function formatTime(t: number) { + if (isNaN(t)) return 'n/a'; + + let h = Math.floor(t / (60 * 60 * 1000)), + m = Math.floor(t / (60 * 1000) % 60), + s = Math.floor(t / 1000 % 60), + ms = Math.floor(t % 1000).toString(); + + while (ms.length < 3) ms = '0' + ms; + + if (h > 0) return `${h}h${m}m${s}.${ms}s`; + if (m > 0) return `${m}m${s}.${ms}s`; + if (s > 0) return `${s}.${ms}s`; + return `${t.toFixed(0)}ms`; + } + + export function log(tag: string, msg: string) { + console.log(`[${tag}] ${msg}`); + } + + export function logId(guid: string, tag: string, msg: string) { + console.log(`[${guid}][${tag}] ${msg}`); + } + + export function error(ctx: string, e: any) { + console.error(`[Error] (${ctx}) ${e}`); + if (e.stack) console.error(e.stack); + } + + + export function errorId(guid: string, e: any) { + console.error(`[${guid}][Error] ${e}`); + if (e.stack) console.error(e.stack); + } +} diff --git a/src/servers/model/config.ts b/src/servers/model/config.ts new file mode 100644 index 0000000000000000000000000000000000000000..97a4d38431a025981696f8cdff6f230528cf1aae --- /dev/null +++ b/src/servers/model/config.ts @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +const config = { + /** + * Determine if and how long to cache entries after a request. + */ + cacheParams: { + useCache: true, + maxApproximateSizeInBytes: 2 * 1014 * 1024 * 1024, // 2 GB + entryTimeoutInMs: 10 * 60 * 1000 // 10 minutes + }, + + /** + * Node (V8) sometimes exhibits GC related issues that significantly slow down the execution + * (https://github.com/nodejs/node/issues/8670). + * + * Therefore an option is provided that automatically shuts down the server. + * For this to work, the server must be run using a deamon (i.e. forever.js on Linux + * or IISnode on Windows) so that the server is automatically restarted when the shutdown happens. + */ + shutdownParams: { + // 0 for off, server will shut down after this amount of minutes. + timeoutMinutes: 24 * 60 /* a day */, + + // modifies the shutdown timer by +/- timeoutVarianceMinutes (to avoid multiple instances shutting at the same time) + timeoutVarianceMinutes: 60 + }, + + defaultPort: 1337, + + /** + * Specify the prefix of the API, i.e. + * <host>/<apiPrefix>/<API queries> + */ + appPrefix: '/ModelServer', + + /** + * The maximum time the server dedicates to executing a query. + * Does not include the time it takes to read and export the data. + */ + maxQueryTimeInMs: 5 * 1000, + + /** + * Maps a request identifier to a filename. + * + * @param source + * Source of the data. + * @param id + * Id provided in the request. + */ + mapFile(source: string, id: string) { + switch (source.toLowerCase()) { + case 'pdb': return `e:/test/quick/${id}_updated.cif`; + default: return void 0; + } + } +}; + +export default config; \ No newline at end of file diff --git a/src/servers/model/local.ts b/src/servers/model/local.ts new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/servers/model/server.ts b/src/servers/model/server.ts new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/servers/model/server/api.ts b/src/servers/model/server/api.ts new file mode 100644 index 0000000000000000000000000000000000000000..21199417c944eec3c7dc3190807eae10235b50af --- /dev/null +++ b/src/servers/model/server/api.ts @@ -0,0 +1,172 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Query, Queries, Structure, Element, StructureSymmetry } from 'mol-model/structure'; + +export enum QueryParamType { + String, + Integer, + Float +} + +export interface QueryParamInfo { + name: string, + type: QueryParamType, + description?: string, + required?: boolean, + defaultValue?: any, + exampleValue?: string, + validation?: (v: any) => void +} + +export interface QueryDefinition { + niceName: string, + exampleId: string, // default is 1cbs + query: (params: any, originalStructure: Structure, transformedStructure: Structure) => Query.Provider, + description: string, + params: QueryParamInfo[], + structureTransform?: (params: any, s: Structure) => Promise<Structure> +} + +const AtomSiteParameters = { + entity_id: <QueryParamInfo>{ name: 'entity_id', type: QueryParamType.String, description: 'Corresponds to the \'_entity.id\' or \'*.label_entity_id\' field, depending on the context.' }, + + label_asym_id: <QueryParamInfo>{ name: 'label_asym_id', type: QueryParamType.String, description: 'Corresponds to the \'_atom_site.label_asym_id\' field.' }, + auth_asym_id: <QueryParamInfo>{ name: 'auth_asym_id', type: QueryParamType.String, exampleValue: 'A', description: 'Corresponds to the \'_atom_site.auth_asym_id\' field.' }, + + label_comp_id: <QueryParamInfo>{ name: 'label_comp_id', type: QueryParamType.String, description: 'Residue name. Corresponds to the \'_atom_site.label_comp_id\' field.' }, + auth_comp_id: <QueryParamInfo>{ name: 'auth_comp_id', type: QueryParamType.String, exampleValue: 'REA', description: 'Author residue name. Corresponds to the \'_atom_site.auth_comp_id\' field.' }, + + pdbx_PDB_ins_code: <QueryParamInfo>{ name: 'pdbx_PDB_ins_code', type: QueryParamType.String, description: 'Corresponds to the \'_atom_site.pdbx_PDB_ins_code\' field.' }, + + label_seq_id: <QueryParamInfo>{ name: 'label_seq_id', type: QueryParamType.Integer, description: 'Residue seq. number. Corresponds to the \'_atom_site.label_seq_id\' field.' }, + auth_seq_id: <QueryParamInfo>{ name: 'auth_seq_id', type: QueryParamType.Integer, exampleValue: '200', description: 'Author residue seq. number. Corresponds to the \'_atom_site.auth_seq_id\' field.' }, +}; + +// function entityTest(params: any): Element.Predicate | undefined { +// if (typeof params.entity_id === 'undefined') return void 0; +// const p = Queries.props.entity.id, id = '' + params.entityId; +// return Element.property(l => p(l) === id); +// } + +function entityTest1_555(params: any): Element.Predicate | undefined { + const oper = Queries.props.unit.operator_name; + if (typeof params.entity_id === 'undefined') return Element.property(l => oper(l) === '1_555'); + const p = Queries.props.entity.id, id = '' + params.entityId; + return Element.property(l => p(l) === id && oper(l) === '1_555'); +} + +function chainTest(params: any): Element.Predicate | undefined { + if (typeof params.label_asym_id !== 'undefined') { + const p = Queries.props.chain.label_asym_id, id = '' + params.label_asym_id; + return Element.property(l => p(l) === id); + } + if (typeof params.auth_asym_id !== 'undefined') { + const p = Queries.props.chain.auth_asym_id, id = '' + params.auth_asym_id; + return Element.property(l => p(l) === id); + } + return void 0; +} + +function residueTest(params: any): Element.Predicate | undefined { + if (typeof params.label_seq_id !== 'undefined') { + const p = Queries.props.residue.label_seq_id, id = +params.label_seq_id; + if (typeof params.pdbx_PDB_ins_code !== 'undefined') { + const p1 = Queries.props.residue.label_seq_id, id1 = params.pdbx_PDB_ins_code; + return Element.property(l => p(l) === id && p1(l) === id1); + } + return Element.property(l => p(l) === id); + } + if (typeof params.auth_seq_id !== 'undefined') { + const p = Queries.props.residue.auth_seq_id, id = +params.auth_seq_id; + if (typeof params.pdbx_PDB_ins_code !== 'undefined') { + const p1 = Queries.props.residue.label_seq_id, id1 = params.pdbx_PDB_ins_code; + return Element.property(l => p(l) === id && p1(l) === id1); + } + return Element.property(l => p(l) === id); + } + return void 0; +} + +// function buildResiduesQuery(params: any): Query.Provider { +// return Queries.generators.atoms({ entityTest: entityTest(params), chainTest: chainTest(params), residueTest: residueTest(params) }); +// } + +const QueryMap: { [id: string]: Partial<QueryDefinition> } = { + 'full': { niceName: 'Full Structure', query: () => Queries.generators.all, description: 'The full structure.' }, + 'residueInteraction': { + niceName: 'Residues Inside a Sphere', + description: 'Identifies all residues within the given radius from the source residue.', + query(p) { + const center = Queries.generators.atoms({ entityTest: entityTest1_555(p), chainTest: chainTest(p), residueTest: residueTest(p) }); + return Queries.modifiers.includeSurroundings(center, { radius: p.radius, wholeResidues: true }); + }, + structureTransform(p, s) { + return StructureSymmetry.builderSymmetryMates(p, p. radius).run(); + }, + params: [ + AtomSiteParameters.entity_id, + AtomSiteParameters.label_asym_id, + AtomSiteParameters.auth_asym_id, + AtomSiteParameters.label_comp_id, + AtomSiteParameters.auth_comp_id, + AtomSiteParameters.pdbx_PDB_ins_code, + AtomSiteParameters.label_seq_id, + AtomSiteParameters.auth_seq_id, + { + name: 'radius', + type: QueryParamType.Float, + defaultValue: 5, + exampleValue: '5', + description: 'Value in Angstroms.', + validation(v: any) { + if (v < 1 || v > 10) { + throw `Invalid radius for residue interaction query (must be a value between 1 and 10).`; + } + } + }, + ] + }, +} + +export function getQueryByName(name: string) { + return QueryMap[name]; +} + +export const QueryList = (function () { + const list: { name: string, definition: QueryDefinition }[] = []; + for (const k of Object.keys(QueryMap)) list.push({ name: k, definition: <QueryDefinition>QueryMap[k] }); + list.sort(function (a, b) { return a.name < b.name ? -1 : a.name > b.name ? 1 : 0 }); + return list; +})(); + +function _normalizeQueryParams(params: { [p: string]: string }, paramList: QueryParamInfo[]): { [p: string]: string | number | boolean } { + const ret: any = {}; + for (const p of paramList) { + const key = p.name; + + if (typeof params[key] === 'undefined' || (params[key] !== null && params[key]['length'] === 0)) { + if (p.required) { + throw `The parameter '${key}' is required.`; + } + ret[key] = p.defaultValue; + } else { + switch (p.type) { + case QueryParamType.String: ret[key] = params[key]; break; + case QueryParamType.Integer: ret[key] = parseInt(params[key]); break; + case QueryParamType.Float: ret[key] = parseFloat(params[key]); break; + } + + if (p.validation) p.validation(ret[key]); + } + } + + return ret; +} + +export function normalizeQueryParams(query: QueryDefinition, params: any) { + return _normalizeQueryParams(params, query.params); +} \ No newline at end of file diff --git a/src/servers/model/server/cache.ts b/src/servers/model/server/cache.ts new file mode 100644 index 0000000000000000000000000000000000000000..b94bdefc2cb373d0a1e533d2e3eaf4217c339e56 --- /dev/null +++ b/src/servers/model/server/cache.ts @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { ConsoleLogger } from 'mol-util/console-logger' +import { LinkedList } from 'mol-data/generic'; +import ServerConfig from '../config'; + +interface CacheEntry<T> { + key: string, + approximateSize: number, + timeoutId: NodeJS.Timer | undefined, + item: T +} + +type CacheNode<T> = LinkedList.Node<CacheEntry<T>> + +export interface CacheParams { + useCache: boolean, + maxApproximateSizeInBytes: number, // = 2 * 1014 * 1024 * 1024; // 2 GB + entryTimeoutInMs: number // = 10 * 60 * 1000; // 10 minutes +} + +export class Cache<T> { + private entries = LinkedList<CacheEntry<T>>(); + private entryMap = new Map<string, CacheNode<T>>(); + private approximateSize = 0; + + private clearTimeout(e: CacheNode<T>) { + if (typeof e.value.timeoutId !== 'undefined') { + clearTimeout(e.value.timeoutId); + e.value.timeoutId = void 0; + } + } + + private dispose(e: CacheNode<T>) { + this.clearTimeout(e); + + if (e.inList) { + this.entries.remove(e); + this.approximateSize -= e.value.approximateSize; + } + this.entryMap.delete(e.value.key); + } + + private refresh(e: CacheNode<T>) { + this.clearTimeout(e); + + e.value.timeoutId = setTimeout(() => this.expire(e), ServerConfig.cacheParams.entryTimeoutInMs); + this.entries.remove(e); + this.entries.addFirst(e.value); + } + + private expire(e: CacheNode<T>, notify = true) { + if (notify) ConsoleLogger.log('Cache', `${e.value.key} expired.`); + this.dispose(e); + } + + expireAll() { + for (let e = this.entries.first; e; e = e.next) this.expire(e, false); + } + + add(item: T) { + const key = this.keyGetter(item); + const approximateSize = this.sizeGetter(item); + + if (this.entryMap.has(key)) this.dispose(this.entryMap.get(key)!); + + if (ServerConfig.cacheParams.maxApproximateSizeInBytes < this.approximateSize + approximateSize) { + if (this.entries.last) this.dispose(this.entries.last); + } + + this.approximateSize += approximateSize; + const entry: CacheEntry<T> = { key, approximateSize, timeoutId: void 0, item }; + const e = this.entries.addFirst(entry); + this.entryMap.set(key, e); + this.refresh(e); + ConsoleLogger.log('Cache', `${key} added.`); + + return item; + } + + has(key: string) { + return this.entryMap.has(key); + } + + + get(key: string) { + if (!this.entryMap.has(key)) return void 0; + let e = this.entryMap.get(key)!; + this.refresh(e); + ConsoleLogger.log('Cache', `${key} accessed.`); + return e.value.item; + } + + constructor(private keyGetter: (i: T) => string, private sizeGetter: (i: T) => number) { + } +} \ No newline at end of file diff --git a/src/servers/model/server/query.ts b/src/servers/model/server/query.ts new file mode 100644 index 0000000000000000000000000000000000000000..718265b10c9b4c9c1bfe1537a2a23a5d58cf7bef --- /dev/null +++ b/src/servers/model/server/query.ts @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { UUID } from 'mol-util'; + +export interface ResponseFormat { + +} + +export interface Query { + id: UUID, + + sourceId: 'file' | string, + entryId: string, + + kind: string, + params: any, + + responseFormat: ResponseFormat +} + +// export class QueryQueue { + +// } + +export function resolveQuery() { + +} \ No newline at end of file diff --git a/src/servers/model/server/structure-wrapper.ts b/src/servers/model/server/structure-wrapper.ts new file mode 100644 index 0000000000000000000000000000000000000000..815d22c8be8e1b20231ee1a241249bc7e6ada492 --- /dev/null +++ b/src/servers/model/server/structure-wrapper.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Structure } from 'mol-model/structure'; + +export enum StructureSourceType { + File, + Cache +} + +export interface StructureInfo { + sourceType: StructureSourceType; + readTime: number; + parseTime: number; + + sourceId: string, + entryId: string, + filename: string +} + +export class StructureWrapper { + info: StructureInfo; + + key: string; + approximateSize: number; + structure: Structure; +} + +export function getStructure(filename: string): Promise<StructureWrapper> +export function getStructure(sourceId: string, entryId: string): Promise<StructureWrapper> +export function getStructure(sourceIdOrFilename: string, entryId?: string): Promise<StructureWrapper> { + return 0 as any; +} \ No newline at end of file diff --git a/src/servers/model/version.ts b/src/servers/model/version.ts new file mode 100644 index 0000000000000000000000000000000000000000..e3941c34a2ff25bc14e17941fe12b356c5b60f2a --- /dev/null +++ b/src/servers/model/version.ts @@ -0,0 +1,7 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +export default '0.1.0'; \ No newline at end of file diff --git a/src/servers/volume/server.ts b/src/servers/volume/server.ts index 7fbb29eff0277feb12c00774aece4595aaa681e1..5d796f6287477133a8aef1894836214b94eeb40a 100644 --- a/src/servers/volume/server.ts +++ b/src/servers/volume/server.ts @@ -12,12 +12,12 @@ import * as compression from 'compression' import init from './server/web-api' import VERSION from './server/version' import ServerConfig from './server-config' -import * as Logger from './server/utils/logger' +import { ConsoleLogger } from 'mol-util/console-logger' import { State } from './server/state' function setupShutdown() { if (ServerConfig.shutdownParams.timeoutVarianceMinutes > ServerConfig.shutdownParams.timeoutMinutes) { - Logger.logPlain('Server', 'Shutdown timeout variance is greater than the timer itself, ignoring.'); + ConsoleLogger.log('Server', 'Shutdown timeout variance is greater than the timer itself, ignoring.'); } else { let tVar = 0; if (ServerConfig.shutdownParams.timeoutVarianceMinutes > 0) { @@ -26,7 +26,7 @@ function setupShutdown() { let tMs = (ServerConfig.shutdownParams.timeoutMinutes + tVar) * 60 * 1000; console.log(`----------------------------------------------------------------------------`); - console.log(` The server will shut down in ${Logger.formatTime(tMs)} to prevent slow performance.`); + console.log(` The server will shut down in ${ConsoleLogger.formatTime(tMs)} to prevent slow performance.`); console.log(` Please make sure a daemon is running that will automatically restart it.`); console.log(`----------------------------------------------------------------------------`); console.log(); @@ -35,7 +35,7 @@ function setupShutdown() { if (State.pendingQueries > 0) { State.shutdownOnZeroPending = true; } else { - Logger.logPlain('Server', `Shut down due to timeout.`); + ConsoleLogger.log('Server', `Shut down due to timeout.`); process.exit(0); } }, tMs); diff --git a/src/servers/volume/server/api.ts b/src/servers/volume/server/api.ts index 147c0e56a7e373ad84dfc35840b0e598702f320c..c1046a4875498e357e62f37450109b264161a797 100644 --- a/src/servers/volume/server/api.ts +++ b/src/servers/volume/server/api.ts @@ -9,7 +9,7 @@ import * as File from '../common/file' import execute from './query/execute' import * as Data from './query/data-model' -import * as Logger from './utils/logger' +import { ConsoleLogger } from 'mol-util/console-logger' import * as DataFormat from '../common/data-format' import ServerConfig from '../server-config' @@ -27,10 +27,10 @@ export function getOutputFilename(source: string, id: string, { asBinary, box, d /** Reads the header and includes information about available detail levels */ export async function getHeaderJson(filename: string | undefined, sourceId: string) { - Logger.logPlain('Header', sourceId); + ConsoleLogger.log('Header', sourceId); try { if (!filename || !File.exists(filename)) { - Logger.errorPlain(`Header ${sourceId}`, 'File not found.'); + ConsoleLogger.error(`Header ${sourceId}`, 'File not found.'); return void 0; } const header = { ...await readHeader(filename, sourceId) } as DataFormat.Header; @@ -47,7 +47,7 @@ export async function getHeaderJson(filename: string | undefined, sourceId: stri (header as any).isAvailable = true; return JSON.stringify(header, null, 2); } catch (e) { - Logger.errorPlain(`Header ${sourceId}`, e); + ConsoleLogger.error(`Header ${sourceId}`, e); return void 0; } } @@ -64,7 +64,7 @@ async function readHeader(filename: string | undefined, sourceId: string) { const header = await DataFormat.readHeader(file); return header.header; } catch (e) { - Logger.errorPlain(`Info ${sourceId}`, e); + ConsoleLogger.error(`Info ${sourceId}`, e); return void 0; } finally { File.close(file); diff --git a/src/servers/volume/server/query/execute.ts b/src/servers/volume/server/query/execute.ts index d31f9ed3048b248294bdffc0390362cc063024ee..d6ecf6ecf675b481cb27569668cb33d3bd2cf782 100644 --- a/src/servers/volume/server/query/execute.ts +++ b/src/servers/volume/server/query/execute.ts @@ -11,7 +11,7 @@ import * as File from '../../common/file' import * as Data from './data-model' import * as Coords from '../algebra/coordinate' import * as Box from '../algebra/box' -import * as Logger from '../utils/logger' +import { ConsoleLogger } from 'mol-util/console-logger' import { State } from '../state' import ServerConfig from '../../server-config' @@ -28,7 +28,7 @@ export default async function execute(params: Data.QueryParams, outputProvider: const guid = UUID.create() as any as string; params.detail = Math.min(Math.max(0, params.detail | 0), ServerConfig.limits.maxOutputSizeInVoxelCountByPrecisionLevel.length - 1); - Logger.log(guid, 'Info', `id=${params.sourceId},encoding=${params.asBinary ? 'binary' : 'text'},detail=${params.detail},${queryBoxToString(params.box)}`); + ConsoleLogger.logId(guid, 'Info', `id=${params.sourceId},encoding=${params.asBinary ? 'binary' : 'text'},detail=${params.detail},${queryBoxToString(params.box)}`); let sourceFile: number | undefined = void 0; try { @@ -36,11 +36,11 @@ export default async function execute(params: Data.QueryParams, outputProvider: await _execute(sourceFile, params, guid, outputProvider); return true; } catch (e) { - Logger.error(guid, e); + ConsoleLogger.errorId(guid, e); return false; } finally { File.close(sourceFile); - Logger.log(guid, 'Time', `${Math.round(getTime() - start)}ms`); + ConsoleLogger.logId(guid, 'Time', `${Math.round(getTime() - start)}ms`); State.pendingQueries--; } } diff --git a/src/servers/volume/server/utils/logger.ts b/src/servers/volume/server/utils/logger.ts deleted file mode 100644 index fc43e21ac6008affc299e33511268999923363bb..0000000000000000000000000000000000000000 --- a/src/servers/volume/server/utils/logger.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Taken/adapted from DensityServer (https://github.com/dsehnal/DensityServer) - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -export function formatTime(t: number) { - if (isNaN(t)) return 'n/a'; - - let h = Math.floor(t / (60 * 60 * 1000)), - m = Math.floor(t / (60 * 1000) % 60), - s = Math.floor(t / 1000 % 60), - ms = Math.floor(t % 1000).toString(); - - while (ms.length < 3) ms = '0' + ms; - - if (h > 0) return `${h}h${m}m${s}.${ms}s`; - if (m > 0) return `${m}m${s}.${ms}s`; - if (s > 0) return `${s}.${ms}s`; - return `${t.toFixed(0)}ms`; -} - -export function logPlain(tag: string, msg: string) { - console.log(`[${tag}] ${msg}`); -} - -export function log(guid: string, tag: string, msg: string) { - console.log(`[${guid}][${tag}] ${msg}`); -} - -export function errorPlain(ctx: string, e: any) { - console.error(`[Error] (${ctx}) ${e}`); - if (e.stack) console.error(e.stack); -} - - -export function error(guid: string, e: any) { - console.error(`[${guid}][Error] ${e}`); - if (e.stack) console.error(e.stack); -} diff --git a/src/servers/volume/server/web-api.ts b/src/servers/volume/server/web-api.ts index 5c4395f06edc530e55ec7908c8ee830cbc823ee5..5040ba702e85fd1c6396f7c3a016fc8295d555eb 100644 --- a/src/servers/volume/server/web-api.ts +++ b/src/servers/volume/server/web-api.ts @@ -14,7 +14,7 @@ import * as Data from './query/data-model' import * as Coords from './algebra/coordinate' import Docs from './documentation' import ServerConfig from '../server-config' -import * as Logger from './utils/logger' +import { ConsoleLogger } from 'mol-util/console-logger' import { State } from './state' export default function init(app: express.Express) { @@ -89,7 +89,7 @@ function validateSourndAndId(req: express.Request, res: express.Response) { if (!req.params.source || req.params.source.length > 32 || !req.params.id || req.params.source.id > 32) { res.writeHead(404); res.end(); - Logger.errorPlain(`Query Box`, 'Invalid source and/or id'); + ConsoleLogger.error(`Query Box`, 'Invalid source and/or id'); return true; } return false; @@ -117,7 +117,7 @@ async function getHeader(req: express.Request, res: express.Response) { headerWritten = true; res.write(header); } catch (e) { - Logger.errorPlain(`Header ${req.params.source}/${req.params.id}`, e); + ConsoleLogger.error(`Header ${req.params.source}/${req.params.id}`, e); if (!headerWritten) { res.writeHead(404); } @@ -171,7 +171,7 @@ async function queryBox(req: express.Request, res: express.Response, params: Dat return; } } catch (e) { - Logger.errorPlain(`Query Box ${JSON.stringify(req.params || {})} | ${JSON.stringify(req.query || {})}`, e); + ConsoleLogger.error(`Query Box ${JSON.stringify(req.params || {})} | ${JSON.stringify(req.query || {})}`, e); response.do404(); } finally { response.end();