Browse Source

storage-node-v2: Add file logging.

Shamil Gadelshin 3 years ago
parent
commit
5411b36055

+ 3 - 4
storage-node-v2/src/commands/dev/multihash.ts

@@ -1,6 +1,7 @@
 import { Command, flags } from '@oclif/command'
 import { hashFile } from '../../services/helpers/hashing'
-import logger, { createStdConsoleLogger } from '../../services/logger'
+import logger from '../../services/logger'
+import { print } from '../../services/helpers/stdout'
 
 /**
  * CLI command:
@@ -32,8 +33,6 @@ export default class DevMultihash extends Command {
 
     logger.info(`Hash: ${multi}`)
 
-    const stdConsoleLogger = createStdConsoleLogger()
-
-    stdConsoleLogger.info(multi)
+    print(multi)
   }
 }

+ 4 - 4
storage-node-v2/src/commands/leader/create-bucket.ts

@@ -1,7 +1,8 @@
 import { createStorageBucket } from '../../services/runtime/extrinsics'
 import { flags } from '@oclif/command'
 import ApiCommandBase from '../../command-base/ApiCommandBase'
-import logger, { createStdConsoleLogger } from '../../services/logger'
+import logger from '../../services/logger'
+import { print } from '../../services/helpers/stdout'
 
 /**
  * CLI command:
@@ -56,9 +57,8 @@ export default class LeaderCreateBucket extends ApiCommandBase {
       objectNumber
     )
     if (success) {
-      const stdConsoleLogger = createStdConsoleLogger()
-
-      stdConsoleLogger.info(bucketId)
+      const castedBucketId = bucketId as number
+      print(castedBucketId.toString())
     }
     this.exitAfterRuntimeCall(success)
   }

+ 8 - 3
storage-node-v2/src/commands/server.ts

@@ -1,7 +1,7 @@
 import { flags } from '@oclif/command'
 import { createApp } from '../services/webApi/app'
 import ApiCommandBase from '../command-base/ApiCommandBase'
-import logger, { initElasticLogger } from '../services/logger'
+import logger, { initNewLogger } from '../services/logger'
 import { loadDataObjectIdCache } from '../services/caching/localDataObjects'
 import { ApiPromise } from '@polkadot/api'
 import { performSync, TempDirName } from '../services/sync/synchronizer'
@@ -75,6 +75,11 @@ export default class Server extends ApiCommandBase {
 Log level could be set using the ELASTIC_LOG_LEVEL enviroment variable.
 Supported values: warn, error, debug, info. Default:debug`,
     }),
+    logFileName: flags.string({
+      char: 'l',
+      required: false,
+      description: `Absolute path to the rolling log file. Creates up to 3 files with 50MB each`,
+    }),
     ...ApiCommandBase.flags,
   }
 
@@ -89,8 +94,8 @@ Supported values: warn, error, debug, info. Default:debug`,
       await loadDataObjectIdCache(flags.uploads, TempDirName)
     }
 
-    if (!_.isEmpty(flags.elasticSearchEndpoint)) {
-      initElasticLogger(logSource, flags.elasticSearchEndpoint ?? '')
+    if (!_.isEmpty(flags.elasticSearchEndpoint) || !_.isEmpty(flags.logFileName)) {
+      initNewLogger({ logSource, elasticSearchEndpoint: flags.elasticSearchEndpoint, filename: flags.logFileName })
     }
 
     logger.info(`Query node endpoint set: ${flags.queryNodeEndpoint}`)

+ 9 - 0
storage-node-v2/src/services/helpers/stdout.ts

@@ -0,0 +1,9 @@
+/**
+ * Prints message to console. We don't use logger in some cases to avoid metadata printing.
+ *
+ * @param msg message to output
+ */
+export function print(msg: string): void {
+  /* eslint-disable no-console */
+  console.log(msg)
+}

+ 49 - 30
storage-node-v2/src/services/logger.ts

@@ -3,7 +3,6 @@ import ecsformat from '@elastic/ecs-winston-format'
 import expressWinston from 'express-winston'
 import { Handler, ErrorRequestHandler } from 'express'
 import { ElasticsearchTransport } from 'winston-elasticsearch'
-
 /**
  * Possible log levels.
  */
@@ -83,13 +82,17 @@ const proxy = new Proxy(InnerLogger, {
 export default proxy
 
 /**
- * Creates Express-Winston logger handler.
+ * Creates Express-Winston logger options.
  * @param logSource - source tag for log entries.
  * @param elasticSearchEndpoint - elastic search engine endpoint (optional).
- * @returns  Express-Winston logger handler
+ * @returns  Express-Winston logger options
  *
  */
-export function httpLogger(logSource: string, elasticSearchEndpoint?: string): Handler {
+export function createExpressLoggerOptions(
+  elasticSearchEndpoint?: string,
+  filename?: string,
+logSource: string
+): expressWinston.LoggerOptions {
   // ElasticSearch server date format.
   const elasticDateFormat = 'YYYY-MM-DDTHH:mm:ss'
 
@@ -100,8 +103,10 @@ export function httpLogger(logSource: string, elasticSearchEndpoint?: string): H
   ]
 
   if (elasticSearchEndpoint) {
-    const esTransport = createElasticTransport(logSource, elasticSearchEndpoint)
-    transports.push(esTransport)
+    transports.push(createElasticTransport(elasticSearchEndpoint, logSource))
+  }
+  if (filename) {
+    transports.push(createFileTransport(filename))
   }
 
   const opts: expressWinston.LoggerOptions = {
@@ -112,47 +117,39 @@ export function httpLogger(logSource: string, elasticSearchEndpoint?: string): H
     colorize: false,
   }
 
-  return expressWinston.logger(opts)
+  return opts
 }
 
 /**
  * Creates Express-Winston error logger.
  *
+ * @param options - express winston logger options.
  * @returns  Express-Winston error logger
  *
  */
-export function errorLogger(): ErrorRequestHandler {
-  return expressWinston.errorLogger({
-    transports: [new winston.transports.Console()],
-    format: winston.format.combine(winston.format.json()),
-  })
+export function errorLogger(options: expressWinston.LoggerOptions): ErrorRequestHandler {
+  return expressWinston.errorLogger(options)
 }
 
 /**
- * Creates clean Console Winston logger for standard output.
+ * Creates Express-Winston logger handler.
  *
- * @returns Winston logger
+ * @param options - express winston logger options.
+ * @returns  Express-Winston logger handler
  *
  */
-export function createStdConsoleLogger(): winston.Logger {
-  const format = winston.format.printf((info) => `${info.message}`)
-
-  const transports = [new winston.transports.Console()]
-
-  return winston.createLogger({
-    levels,
-    format,
-    transports,
-  })
+export function httpLogger(options: expressWinston.LoggerOptions): Handler {
+  return expressWinston.logger(options)
 }
+
 /**
- * Creates Winston logger with Elastic search.
+ * Creates Winston logger with ElasticSearch and File transports.
  * @param logSource - source tag for log entries.
  * @param elasticSearchEndpoint - elastic search engine endpoint.
  * @returns Winston logger
  *
  */
-function createElasticLogger(logSource: string, elasticSearchEndpoint: string): winston.Logger {
+function createCustomLogger(customOptions: { logSource: string, elasticSearchEndpoint?: string; filename?: string }): winston.Logger {
   const loggerOptions = createDefaultLoggerOptions()
 
   // Transports
@@ -161,8 +158,12 @@ function createElasticLogger(logSource: string, elasticSearchEndpoint: string):
     transports = Array.isArray(loggerOptions.transports) ? loggerOptions.transports : [loggerOptions.transports]
   }
 
-  const esTransport = createElasticTransport(logSource, elasticSearchEndpoint)
-  transports.push(esTransport)
+  if (customOptions.elasticSearchEndpoint) {
+    transports.push(createElasticTransport(logSource, customOptions.elasticSearchEndpoint))
+  }
+  if (customOptions.filename) {
+    transports.push(createFileTransport(customOptions.filename))
+  }
 
   // Logger
   const logger = winston.createLogger(loggerOptions)
@@ -182,9 +183,10 @@ function createElasticLogger(logSource: string, elasticSearchEndpoint: string):
  *
  * @param logSource - source tag for log entries.
  * @param elasticSearchEndpoint - elastic search engine endpoint.
+ * @param filename - absolute path to the log file.
  */
-export function initElasticLogger(logSource: string, elasticSearchEndpoint: string): void {
-  InnerLogger = createElasticLogger(logSource, elasticSearchEndpoint)
+export function initNewLogger(options: { logSource: string,elasticSearchEndpoint?: string; filename?: string }): void {
+  InnerLogger = createCustomLogger(options)
 }
 
 /**
@@ -213,3 +215,20 @@ function createElasticTransport(logSource: string, elasticSearchEndpoint: string
   }
   return new ElasticsearchTransport(esTransportOpts)
 }
+
+/**
+ * Creates winston logger file transport.
+ *
+ * @param fileName - log file name.
+ * @returns winston file transport
+ */
+function createFileTransport(filename: string): winston.transport {
+  const options = {
+    filename,
+    maxsize: 50000000, // 50 MB
+    maxFiles: 3,
+    level: 'debug',
+    format: ecsformat(),
+  }
+  return new winston.transports.File(options)
+}

+ 5 - 3
storage-node-v2/src/services/webApi/app.ts

@@ -8,8 +8,8 @@ import { KeyringPair } from '@polkadot/keyring/types'
 import { ApiPromise } from '@polkadot/api'
 import { RequestData, verifyTokenSignature, parseUploadToken, UploadToken } from '../helpers/auth'
 import { checkRemoveNonce } from '../caching/tokenNonceKeeper'
-import { httpLogger, errorLogger } from '../../services/logger'
 import { AppConfig } from './controllers/common'
+import { createExpressLoggerOptions, httpLogger, errorLogger } from '../../services/logger'
 
 /**
  * Creates Express web application. Uses the OAS spec file for the API.
@@ -20,10 +20,11 @@ import { AppConfig } from './controllers/common'
 export async function createApp(config: AppConfig): Promise<Express> {
   const spec = path.join(__dirname, './../../api-spec/openapi.yaml')
   const app = express()
+  const expressLoggerOptions = createExpressLoggerOptions(config.elasticSearchEndpoint, config.logSource)
 
   app.use(cors())
   app.use(express.json())
-  app.use(httpLogger(config.logSource, config.elasticSearchEndpoint))
+  app.use(httpLogger(expressLoggerOptions))
 
   app.use(
     // Set parameters for each request.
@@ -56,7 +57,8 @@ export async function createApp(config: AppConfig): Promise<Express> {
     })
   ) // Required signature.
 
-  app.use(errorLogger())
+  // Error logger
+  app.use(errorLogger(expressLoggerOptions))
 
   /* eslint-disable @typescript-eslint/no-unused-vars */
   app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {