Skip to content

Commit 514c1a5

Browse files
Getting Log working ;)
1 parent 628615a commit 514c1a5

File tree

11 files changed

+1109
-14
lines changed

11 files changed

+1109
-14
lines changed

bin/cli.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,54 @@ const argv = yargs
5050
default: false
5151
}
5252
})
53+
.command('log [client] [instance]', 'Stream log files from an instance', {
54+
interval: {
55+
describe: 'Polling interval (seconds)',
56+
default: 2
57+
},
58+
lines: {
59+
describe: 'Number of lines to display',
60+
default: 100
61+
},
62+
include: {
63+
describe: 'Log levels to include',
64+
type: 'array',
65+
default: []
66+
},
67+
exclude: {
68+
describe: 'Log levels to exclude',
69+
type: 'array',
70+
default: []
71+
},
72+
list: {
73+
describe: 'Output a list of available log levels',
74+
default: false
75+
},
76+
dates: {
77+
describe: 'Output a list of available log dates',
78+
default: false
79+
},
80+
filter: {
81+
describe: 'Filter log messages by regexp',
82+
default: null
83+
},
84+
length: {
85+
describe: 'Length to truncate a log message',
86+
default: null
87+
},
88+
search: {
89+
describe: 'Search all log files',
90+
default: false
91+
},
92+
'no-timestamp': {
93+
describe: "Don't convert timestamps to local time",
94+
default: false
95+
},
96+
latest: {
97+
describe: 'Show Latest Logs Only',
98+
boolean: false
99+
}
100+
})
53101
.example('$0 delete my-client sandbox', 'Delete my-client sandbox config')
54102
.example('$0 watch my-client sandbox', 'Watch for my-client sandbox changes')
55103
.demand(1)

commands/log.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
const argv = require('minimist')(process.argv.slice(2))
2+
const chalk = require('chalk')
3+
const forEach = require('lodash/forEach')
4+
const groupBy = require('lodash/groupBy')
5+
const keys = require('lodash/keys')
6+
const pickBy = require('lodash/pickBy')
7+
const sortBy = require('lodash/sortBy')
8+
9+
const find = require('../lib/find')
10+
const search = require('../lib/search')
11+
const tail = require('../lib/tail')
12+
const config = require('../lib/config')()
13+
14+
module.exports = async options => {
15+
let client = argv['_'][1] || null
16+
let instance = argv['_'][2] || null
17+
let selected = null
18+
19+
// Get Client & Instance, or check for Default
20+
if (client && instance) {
21+
selected = config.get(client, instance)
22+
} else {
23+
const defaultConfig = config.get(client, instance, true)
24+
25+
if (defaultConfig) {
26+
client = defaultConfig.client
27+
instance = defaultConfig.instance
28+
selected = defaultConfig.config
29+
}
30+
}
31+
32+
if (selected) {
33+
let files = await find('Logs', {
34+
baseURL: `https://${selected.h}/on/demandware.servlet/webdav/Sites/`,
35+
auth: {
36+
username: selected.u,
37+
password: selected.p
38+
}
39+
})
40+
41+
files = files.filter(({displayname}) => displayname.includes('.log'))
42+
43+
// Group Logs
44+
let groups = groupBy(files, ({displayname}) => displayname.split('-blade')[0])
45+
let dates = groupBy(files, ({creationdate}) => creationdate.split('T')[0])
46+
47+
// pick out logs we want to include
48+
if (options.include.length > 0) {
49+
if (options.include.length === 1 && options.include[0].includes(',')) {
50+
options.include = options.include[0].split(',')
51+
}
52+
groups = pickBy(
53+
groups,
54+
(group, name) =>
55+
options.include.filter(level => {
56+
return new RegExp(level).test(name)
57+
}).length > 0
58+
)
59+
}
60+
61+
// pick out logs we want to exclude
62+
if (options.exclude.length > 0) {
63+
if (options.exclude.length === 1 && options.exclude[0].includes(',')) {
64+
options.exclude = options.exclude[0].split(',')
65+
}
66+
groups = pickBy(
67+
groups,
68+
(group, name) =>
69+
options.exclude.filter(level => {
70+
return new RegExp(level).test(name)
71+
}).length === 0
72+
)
73+
}
74+
75+
// get list of log types
76+
if (options.list) {
77+
console.log(chalk.green.bold('\nLog Levels:\n'))
78+
79+
forEach(keys(groups).sort(), group => {
80+
console.log('· ' + group)
81+
})
82+
83+
console.log('')
84+
85+
process.exit()
86+
}
87+
88+
// get lost of log dates
89+
if (options.dates) {
90+
console.log(chalk.green.bold('\nLog Dates:\n'))
91+
92+
forEach(
93+
keys(dates)
94+
.sort()
95+
.reverse(),
96+
date => {
97+
console.log('· ' + date)
98+
}
99+
)
100+
101+
console.log('')
102+
103+
process.exit()
104+
}
105+
106+
// setup logs
107+
const logs = []
108+
forEach(groups, (files, name) => {
109+
logs[name] = []
110+
})
111+
112+
// sort groups by last modified
113+
forEach(groups, (files, name) => {
114+
var sorted = sortBy(files, file => new Date(file.getlastmodified)).reverse()
115+
groups[name] = options.latest ? [sorted[0]] : sorted
116+
})
117+
118+
try {
119+
// Start log output
120+
options.search ? search(selected, groups, options) : tail(selected, logs, groups, options)
121+
} catch (err) {
122+
console.log(err)
123+
}
124+
}
125+
}

commands/watch.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,27 @@ const upload = require('../lib/upload')
1212
module.exports = options => {
1313
let client = argv['_'][1] || null
1414
let instance = argv['_'][2] || null
15-
let watching = null
15+
let selected = null
1616
let errorMessage
1717
let logMessage
1818

1919
const useLog = options.log
2020
const errorsOnly = options.errorsOnly
2121

22+
// Get Client & Instance, or check for Default
2223
if (client && instance) {
23-
watching = config.get(client, instance)
24+
selected = config.get(client, instance)
2425
} else {
2526
const defaultConfig = config.get(client, instance, true)
2627

2728
if (defaultConfig) {
2829
client = defaultConfig.client
2930
instance = defaultConfig.instance
30-
watching = defaultConfig.config
31+
selected = defaultConfig.config
3132
}
3233
}
3334

34-
if (watching) {
35+
if (selected) {
3536
let spinner
3637
const watcher = chokidar.watch('dir', {
3738
ignored: [/[/\\]\./, '**/node_modules/**'],
@@ -42,14 +43,14 @@ module.exports = options => {
4243
})
4344

4445
// Add Instance Directory to Watch List
45-
watcher.add(watching.d)
46+
watcher.add(selected.d)
4647

4748
// Watch for File Changes
4849
watcher.on('change', file => {
49-
upload({file, spinner, watching, client, instance, options})
50+
upload({file, spinner, selected, client, instance, options})
5051
})
5152
watcher.on('add', file => {
52-
upload({file, spinner, watching, client, instance, options})
53+
upload({file, spinner, selected, client, instance, options})
5354
})
5455

5556
// @TODO: Watch for Removing Files

lib/find.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const axios = require('axios')
2+
const path = require('path')
3+
const {parseString} = require('xml2js')
4+
const get = require('lodash/get')
5+
const forEach = require('lodash/forEach')
6+
7+
module.exports = async (file, options) => {
8+
try {
9+
const {data} = await axios(
10+
Object.assign({}, options, {
11+
headers: {
12+
Depth: 1
13+
},
14+
url: path.isAbsolute(file) ? file : `/${file}`,
15+
method: 'PROPFIND'
16+
})
17+
)
18+
return await new Promise((resolve, reject) => {
19+
parseString(data, (err, res) => {
20+
if (err) {
21+
return reject(err)
22+
}
23+
24+
resolve(
25+
res.multistatus.response.map(file => {
26+
const info = get(file, 'propstat.0.prop.0')
27+
forEach(info, (value, name) => {
28+
info[name] = get(value, '0')
29+
})
30+
return info
31+
})
32+
)
33+
})
34+
})
35+
} catch (err) {
36+
console.log(err)
37+
return new Error(err)
38+
}
39+
}

lib/read.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const path = require('path')
2+
const axios = require('axios')
3+
4+
module.exports = async (file, options) => {
5+
const {data} = await axios(
6+
Object.assign({}, options, {
7+
url: path.isAbsolute(file) ? file : `/${file}`,
8+
method: 'GET'
9+
})
10+
)
11+
12+
return data
13+
}

lib/search.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
const chalk = require('chalk')
2+
const compact = require('lodash/compact')
3+
const map = require('lodash/map')
4+
const ora = require('ora')
5+
const truncate = require('lodash/truncate')
6+
7+
const read = require('./read')
8+
9+
module.exports = async (selected, groups, options) => {
10+
const text = 'Searching'
11+
12+
const spinner = ora(text)
13+
const output = fn => {
14+
spinner.stop()
15+
fn()
16+
spinner.start()
17+
}
18+
19+
const promiseGroups = map(groups, (files, name) => {
20+
return map(files, async file => {
21+
const displayname = file.displayname
22+
try {
23+
const response = await read(`Logs/${displayname}`, {
24+
baseURL: `https://${selected.h}/on/demandware.servlet/webdav/Sites/`,
25+
auth: {
26+
username: selected.u,
27+
password: selected.p
28+
}
29+
})
30+
return {
31+
response,
32+
name
33+
}
34+
} catch (err) {
35+
output(() => console.log(err))
36+
}
37+
})
38+
})
39+
40+
for (const promises of promiseGroups) {
41+
const results = await Promise.all(promises)
42+
43+
for (const {response, name} of compact(results)) {
44+
const lines = response.split('\n').slice(-options.lines)
45+
46+
for (let line of lines) {
47+
if (line) {
48+
if (!options.noTimestamp) {
49+
line = line.replace(/\[(.+)\sGMT\]/g, (exp, match) => {
50+
const date = new Date(Date.parse(match + 'Z'))
51+
return chalk.magenta(`[${date.toLocaleDateString()} ${date.toLocaleTimeString()}]`)
52+
})
53+
}
54+
// if there's a filter and it doesn't pass .,., the ignore line
55+
if (options.filter && !new RegExp(options.filter, 'ig').test(line)) {
56+
continue
57+
}
58+
// highlight the matching parts of the line
59+
if (options.filter) {
60+
line = line.replace(new RegExp(options.filter, 'ig'), exp => {
61+
return chalk.yellow(exp)
62+
})
63+
}
64+
65+
if (options.length > 0) {
66+
line = truncate(line.trim(), {
67+
length: options.length,
68+
omission: ''
69+
})
70+
}
71+
72+
output(() => console.log(`${chalk.white(name)} ${line}`, 'blue'))
73+
}
74+
}
75+
}
76+
}
77+
78+
// spinner.stop();
79+
spinner.text = `Search ` + (options.filter ? `for '${options.filter}' ` : '') + `complete`
80+
spinner.succeed()
81+
process.exit()
82+
}

0 commit comments

Comments
 (0)