Skip to content
This repository has been archived by the owner on Nov 3, 2020. It is now read-only.

Commit

Permalink
feat: add a logger mechanism (#62)
Browse files Browse the repository at this point in the history
  • Loading branch information
arkaitzgarro authored Jul 3, 2018
1 parent ae706a0 commit 47bf5a9
Show file tree
Hide file tree
Showing 17 changed files with 372 additions and 20 deletions.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,37 @@ await Promise.all(
);
```

## Logging levels

Logging levels in this SDK conform to the severity ordering specified by [RFC5424]: _severity of all levels is assumed to be numerically **ascending** from most important to least important._

Each `level` is given a specific integer priority. The higher the priority the more important the message is considered to be, and the lower the corresponding integer priority.

``` js
{
error: 0,
warn: 1,
info: 2,
verbose: 3,
debug: 4,
silly: 5
}
```

We make use of the `npm` levels showed above.

Setting the level for your logging message can be done providing the value when creating WeTransfer. For example, using the `warn` level you could log `warn` messages and below to the console, which in this case will only be `warn` and `error`.

```js
const apiClient = await createWTClient('/* YOUR PRIVATE API KEY GOES HERE*/', {
logger: {
level: 'debug'
}
});
```

If no value is provided, by default we will use `info`.

## Documentation

Visit [https://developers.wetransfer.com/documentation](https://developers.wetransfer.com/documentation) to access the latest API related documentation.
Expand Down
24 changes: 24 additions & 0 deletions __tests__/config/__snapshots__/logger.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Logger configuration when setting a level that doesn't exist should keep default info level 1`] = `
Array [
"[WeTransfer SDK] \\"yolo\\" is not a valid logger level. Please specify one of the following levels: [
 'error',
 'warning',
 'info',
 'verbose,'
 'debug',
 'silly'
 ]
",
]
`;

exports[`Logger configuration when setting an empty level should keep default info level 1`] = `undefined`;

exports[`Logger configuration when silly level is set should log silly logs 1`] = `
Array [
"[WeTransfer SDK] This is a silly log
",
]
`;
48 changes: 48 additions & 0 deletions __tests__/config/logger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const logger = require('../../src/config/logger');

describe('Logger configuration', () => {
let log;
beforeEach(() => {
logger.setLoggerLevel('info');
log = jest.spyOn(console._stdout, 'write').mockImplementation(() => {
/* don't console.log anything */
});
});

afterEach(() => {
log.mockRestore();
});

describe('when silly level is set', () => {
it('should log silly logs', () => {
logger.setLoggerLevel('silly');
logger.silly('This is a silly log');
expect(log.mock.calls[log.mock.calls.length - 1]).toMatchSnapshot();
});
});

describe('when error level is set', () => {
it('should NOT log silly logs', () => {
logger.setLoggerLevel('error');
logger.silly('This is a silly log');
expect(log).not.toHaveBeenCalled();
});
});

describe('when setting a level that doesn\'t exist', () => {
it('should keep default info level', () => {
logger.setLoggerLevel('yolo');
expect(log.mock.calls[log.mock.calls.length - 1]).toMatchSnapshot();
expect(logger.transports[0].level).toBe('info');
});
});

describe('when setting an empty level', () => {
it('should keep default info level', () => {
expect(logger.transports[0].level).toBe('info');
logger.setLoggerLevel();
expect(log.mock.calls[log.mock.calls.length - 1]).toMatchSnapshot();
expect(logger.transports[0].level).toBe('info');
});
});
});
7 changes: 6 additions & 1 deletion __tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ describe('createWTClient function', () => {
});

it('should return a client if API KEY is provided', async () => {
const apiClient = await createWTClient('super-secret-api-key');
const apiClient = await createWTClient('super-secret-api-key', {
logger: {
level: 'error'
}
});

expect(apiClient).toEqual({
authorize: expect.any(Function),
transfer: {
Expand Down
6 changes: 5 additions & 1 deletion example/create-transfer.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ const data = {
);

try {
const apiClient = await createWTClient(process.env.WT_API_KEY);
const apiClient = await createWTClient(process.env.WT_API_KEY, {
logger: {
level: 'silly'
}
});

const transfer = await apiClient.transfer.create(data.transfer);
const transferFiles = await apiClient.transfer.addFiles(
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
},
"dependencies": {
"axios": "0.18.0",
"lodash": "4.17.10"
"lodash": "4.17.10",
"winston": "3.0.0"
},
"devDependencies": {
"@wetransfer/eslint-config-wetransfer": "2.0.1",
Expand All @@ -51,6 +52,9 @@
},
"jest": {
"testEnvironment": "node",
"setupFiles": [
"./scripts/jest/setup.js"
],
"watchPlugins": [
"jest-watch-typeahead/filename",
"jest-watch-typeahead/testname"
Expand Down
3 changes: 3 additions & 0 deletions scripts/jest/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const logger = require('../../src/config/logger');

logger.setLoggerLevel('error');
2 changes: 2 additions & 0 deletions src/authorize/authorize.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
const WTError = require('../error');
const logger = require('../config/logger');

module.exports = function({ request, routes }) {
return async function authorize() {
try {
logger.info('Authorizing your API key.');
return await request.send(routes.authorize);
} catch (error) {
throw new WTError(
Expand Down
45 changes: 45 additions & 0 deletions src/config/logger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const { includes } = require('lodash');
const winston = require('winston');
const { createLogger, config, format } = winston;
const { colorize, combine, label, printf } = format;

const customFormat = printf((info) => `[${info.label}] ${info.message}`);

const transports = {
console: new winston.transports.Console({ level: 'info' })
};

const logger = createLogger({
levels: config.npm.levels,
format: combine(
colorize({ all: true }),
label({ label: 'WeTransfer SDK' }),
customFormat
),
transports: [transports.console]
});

winston.addColors({
error: 'red',
warn: 'yellow',
info: 'white',
debug: 'white'
});

function setLoggerLevel(level = 'info') {
if (!includes(Object.keys(config.npm.levels), level)) {
return logger.warn(`"${level}" is not a valid logger level. Please specify one of the following levels: [
'error',
'warning',
'info',
'verbose,'
'debug',
'silly'
]`);
}

transports.console.level = level;
}

module.exports = logger;
module.exports.setLoggerLevel = setLoggerLevel;
13 changes: 11 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
const authorize = require('./authorize');
const { get } = require('lodash');

const WTError = require('./error');
const logger = require('./config/logger');

const authorize = require('./authorize');
const request = require('./request');
const { create } = require('./transfer');
const {
Expand All @@ -10,11 +14,16 @@ const {
completeFileUpload
} = require('./items');

module.exports = async function createWTClient(apiKey) {
module.exports = async function createWTClient(
apiKey,
options = { logger: {} }
) {
if (!apiKey) {
throw new WTError('No API Key provided');
}

logger.setLoggerLevel(get(options, 'logger.level', 'info'));

request.apiKey = apiKey;
request.jwt = (await authorize()).token;

Expand Down
6 changes: 6 additions & 0 deletions src/items/actions/add-files.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const logger = require('../../config/logger');

module.exports = function({ sendItems, futureFile, RemoteFile }) {
/**
* Add files to an existing transfer.
Expand All @@ -6,6 +8,10 @@ module.exports = function({ sendItems, futureFile, RemoteFile }) {
* @returns {Promise} A collection of created items
*/
return async function addFiles(transfer, files) {
logger.info(
`Adding ${files.length} files to transfer with ID ${transfer.id}`
);

const transferItems = (await sendItems(
transfer,
files.map(futureFile)
Expand Down
6 changes: 4 additions & 2 deletions src/items/actions/add-items.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const logger = require('../../config/logger');

module.exports = function({ sendItems, normalizeItem, normalizeResponseItem }) {
/**
* Add items to an existing transfer.
Expand All @@ -6,8 +8,8 @@ module.exports = function({ sendItems, normalizeItem, normalizeResponseItem }) {
* @returns {Promise} A collection of created items
*/
return async function addItems(transferId, items) {
console.warn(
'[DEPRECATED WARNING]: addItems method will be removed in future versions. Please use addFiles or addLinks methods instead.'
logger.warn(
'[DEPRECATED]: addItems method will be removed in future versions. Please use addFiles or addLinks methods instead.'
);
return (await sendItems({ id: transferId }, items.map(normalizeItem))).map(
normalizeResponseItem
Expand Down
6 changes: 6 additions & 0 deletions src/items/actions/add-links.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const logger = require('../../config/logger');

module.exports = function({ sendItems, futureLink, RemoteLink }) {
/**
* Add links to an existing transfer.
Expand All @@ -6,6 +8,10 @@ module.exports = function({ sendItems, futureLink, RemoteLink }) {
* @returns {Promise} A collection of created items
*/
return async function addLinks(transfer, links) {
logger.info(
`Adding ${links.length} links to transfer with ID ${transfer.id}`
);

const transferItems = (await sendItems(
transfer,
links.map(futureLink)
Expand Down
49 changes: 39 additions & 10 deletions src/items/actions/upload-file.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const logger = require('../../config/logger');
const WTError = require('../../error');

module.exports = function({ request, routes }) {
Expand All @@ -17,9 +18,17 @@ module.exports = function({ request, routes }) {
* @returns {Promise} Empty response if everything goes well 🤔
*/
function uploadPart(file, data, partNumber) {
logger.debug(
`[${file.name}] Requesting S3 upload URL for part #${partNumber}`
);
return request
.send(routes.multipart(file, partNumber))
.then((multipartItem) => {
logger.debug(
`[${file.name}] Uploading ${
data.length
} bytes for part #${partNumber} to S3`
);
return request.upload(multipartItem.upload_url, data);
});
}
Expand All @@ -32,19 +41,34 @@ module.exports = function({ request, routes }) {
* @returns {Array} Array of part upload promises
*/
async function uploadAllParts(file, content) {
for (
let partNumber = 0;
partNumber < file.meta.multipart_parts;
partNumber++
) {
const totalParts = file.meta.multipart_parts;
logger.debug(
`[${
file.name
}] Splitting file into ${totalParts} parts. Total size to upload: ${
content.length
} bytes.`
);

for (let partNumber = 0; partNumber < totalParts; partNumber++) {
const chunkStart = partNumber * MAX_CHUNK_SIZE;
const chunkEnd = (partNumber + 1) * MAX_CHUNK_SIZE;

logger.debug(
`[${file.name}] Part #${partNumber +
1} of ${totalParts}. Bytes from ${chunkStart} to ${chunkEnd}.`
);

await uploadPart(
file,
content.slice(
partNumber * MAX_CHUNK_SIZE,
(partNumber + 1) * MAX_CHUNK_SIZE
),
content.slice(chunkStart, chunkEnd),
partNumber + 1
);

logger.debug(
`[${file.name}] Uploaded part #${partNumber +
1} of ${totalParts} to S3"`
);
}
}

Expand All @@ -56,9 +80,14 @@ module.exports = function({ request, routes }) {
* @returns {Promise} Empty response if everything goes well 🤔
*/
return async function uploadFile(file, content) {
logger.info(`[${file.name}] Starting file upload.`);

try {
await uploadAllParts(file, content);
return await completeFileUpload(file);
const response = await completeFileUpload(file);
logger.info(`[${file.name}] File upload complete.`);

return response;
} catch (error) {
throw new WTError(
`There was an error when uploading ${file.name}.`,
Expand Down
Loading

0 comments on commit 47bf5a9

Please sign in to comment.