UNPKG

4.51 kBJavaScriptView Raw
1'use strict';
2
3const BbPromise = require('bluebird');
4const chalk = require('chalk');
5const moment = require('moment');
6const path = require('path');
7
8class OpenWhiskLogs {
9 constructor(serverless, options) {
10 this.serverless = serverless;
11 this.options = options || {};
12 this.provider = this.serverless.getProvider('openwhisk');
13 this.previous_activations = new Set()
14
15 this.hooks = {
16 'logs:logs': () => BbPromise.bind(this)
17 .then(this.validate)
18 .then(this.functionLogs)
19 };
20 }
21
22 validate () {
23 if (!this.serverless.config.servicePath) {
24 throw new this.serverless.classes.Error('This command can only be run inside a service.');
25 }
26
27 this.serverless.service.getFunction(this.options.function);
28
29 this.options.stage = this.options.stage
30 || (this.serverless.service.provider && this.serverless.service.provider.stage)
31 || 'dev';
32 this.options.region = this.options.region
33 || (this.serverless.service.provider && this.serverless.service.provider.region)
34 || 'us-east-1';
35
36 this.options.interval = this.options.interval || 1000;
37
38 if (this.options.filter) {
39 this.options.filter = new RegExp(this.options.filter, 'i');
40 }
41
42 if (this.options.startTime) {
43 this.options.startTime = moment(this.options.startTime)
44 }
45
46 return this.provider.client().then(client => {
47 this.client = client;
48 });
49 }
50
51 functionLogs () {
52 return BbPromise.bind(this)
53 .then(this.retrieveInvocationLogs)
54 .then(this.filterFunctionLogs)
55 .then(this.showFunctionLogs)
56 .then(() => {
57 if (this.options.tail) {
58 this.timeout = setTimeout(() => {
59 this.functionLogs()
60 }, this.options.interval)
61 }
62 })
63 }
64
65 retrieveInvocationLogs () {
66 const functionObject = this.serverless.service.getFunction(this.options.function);
67
68 const options = {
69 docs: true,
70 limit: 100,
71 namespace: '_'
72 };
73
74 return this.client.activations.list(options)
75 .catch(err => {
76 throw new this.serverless.classes.Error(
77 `Failed to retrieve activation logs due to error:`
78 + ` ${err.message}`
79 );
80 });
81 }
82
83 filterFunctionLogs (logs) {
84 const functionObject = this.serverless.service.getFunction(this.options.function);
85 const actionName = functionObject.name || `${this.serverless.service.service}_${this.options.function}`
86
87 // skip activations for other actions or that we have seen before
88 const filtered = logs.filter(log => log.name === actionName
89 && !this.previous_activations.has(log.activationId))
90
91 // allow regexp filtering of log messages
92 if (this.options.filter) {
93 filtered.forEach(log => {
94 log.logs = log.logs.filter(logLine => logLine.match(this.options.filter))
95 })
96 }
97
98 // filter those logs based upon start time
99 if (this.options.startTime) {
100 filtered.forEach(log => {
101 log.logs = log.logs.filter(logLine => {
102 const timestamp = logLine.split(" ")[0]
103 return this.options.startTime.isBefore(moment(timestamp))
104 })
105 })
106 }
107
108 return BbPromise.resolve(filtered);
109 }
110
111 showFunctionLogs (logs) {
112 if (!this.options.tail && !logs.length) {
113 this.consoleLog(`There's no log data for function "${this.options.function}" available right now…`)
114 return BbPromise.resolve();
115 }
116
117 logs.filter(log => log.logs.length)
118 .reverse()
119 .map((log, idx, arr) => {
120 if (this.timeout && idx === 0) console.log('')
121
122 this.previous_activations.add(log.activationId)
123 this.consoleLog(this.formatActivationLine(log))
124 log.logs.map(this.formatLogLine).forEach(this.consoleLog)
125
126 if (idx != (arr.length - 1)) {
127 this.consoleLog('')
128 }
129 })
130 return BbPromise.resolve();
131 }
132
133 formatActivationLine (activation) {
134 return `${chalk.blue('activation')} (${chalk.yellow(activation.activationId)}):`
135 }
136
137 formatLogLine (logLine) {
138 const items = logLine.split(' ').filter(item => item !== '')
139 const format = 'YYYY-MM-DD HH:mm:ss.SSS'
140 const timestamp = chalk.green(moment(items[0]).utc().format(format))
141
142 let contents = items.slice(2).join(' ')
143 if (items[1] === 'stderr:') {
144 contents = chalk.red(contents)
145 }
146
147 return `${timestamp} ${contents}`
148 }
149
150 consoleLog(msg) {
151 console.log(msg); // eslint-disable-line no-console
152 }
153}
154
155module.exports = OpenWhiskLogs;