Skip to content

Commit 6079ca0

Browse files
authored
feat: adding debug logger (#64)
1 parent f1e8205 commit 6079ca0

13 files changed

+300
-45
lines changed

README.md

+20
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,26 @@ import { getAuthenticatorFromEnvironment } from 'ibm-cloud-sdk-core';
5959
const iamAuthenticator = getAuthenticatorFromEnvironment('my-service');
6060
```
6161

62+
## Logging
63+
This package uses [debug](https://www.npmjs.com/package/debug) for logging.
64+
65+
- Logging is disabled by default.
66+
- Logging has been configured to use log levels which are assumed to be numerically ascending from most important to least important.
67+
- In order to see the log output, set the environment variable ``DEBUG`` including the desired log level.
68+
- ```DEBUG=ibm-cloud-sdk-core:error``` enables error logs
69+
- ```DEBUG=ibm-cloud-sdk-core:warning``` enables warning logs and below
70+
- ```DEBUG=ibm-cloud-sdk-core:info``` enables info logs and below
71+
- ```DEBUG=ibm-cloud-sdk-core:verbose``` enables verbose logs and below
72+
- ```DEBUG=ibm-cloud-sdk-core:debug``` enables debug logs and below
73+
74+
To see the output from all of the debugging levels you can use:
75+
76+
``DEBUG=ibm-cloud-sdk-core*``
77+
78+
The debug logger can be configured to be used for more than one library. In example, you can set a comma-separated string:
79+
80+
``DEBUG=ibm-cloud-sdk-core:debug,other-lib:debug``
81+
6282
## Issues
6383
If you encounter an issue with this project, you are welcome to submit a [bug report](https://github.com/IBM/node-sdk-core/issues).
6484
Before opening a new issue, please search for similar issues. It's possible that someone has already reported it.

auth/token-managers/iam-token-manager.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import extend = require('extend');
1818
import { OutgoingHttpHeaders } from 'http';
1919
import { getMissingParams } from '../../lib/helper';
20+
import logger from '../../lib/logger';
2021
import { computeBasicAuthHeader, validateInput } from '../utils';
2122
import { JwtTokenManager, TokenManagerOptions } from './jwt-token-manager';
2223

@@ -43,7 +44,7 @@ interface Options extends TokenManagerOptions {
4344
}
4445

4546
// this interface is a representation of the response
46-
// object from the IAM service, hence the snake_case
47+
// object from the IAM service, hence the snake_case
4748
// parameter names
4849
export interface IamTokenData {
4950
access_token: string;
@@ -74,7 +75,7 @@ export class IamTokenManager extends JwtTokenManager {
7475
super(options);
7576

7677
validateInput(options, this.requiredOptions);
77-
78+
7879
this.apikey = options.apikey;
7980

8081
this.url = this.url || 'https://iam.cloud.ibm.com/identity/token';
@@ -87,7 +88,7 @@ export class IamTokenManager extends JwtTokenManager {
8788
}
8889
if (onlyOne(options.clientId, options.clientSecret)) {
8990
// tslint:disable-next-line
90-
console.log(CLIENT_ID_SECRET_WARNING);
91+
logger.warn(CLIENT_ID_SECRET_WARNING);
9192
}
9293
}
9394

@@ -98,7 +99,7 @@ export class IamTokenManager extends JwtTokenManager {
9899
* If these values are not set, no Authorization header will be
99100
* set on the request (it is not required).
100101
*
101-
* @param {string} clientId - The client id
102+
* @param {string} clientId - The client id
102103
* @param {string} clientSecret - The client secret
103104
* @returns {void}
104105
*/
@@ -107,7 +108,7 @@ export class IamTokenManager extends JwtTokenManager {
107108
this.clientSecret = clientSecret;
108109
if (onlyOne(clientId, clientSecret)) {
109110
// tslint:disable-next-line
110-
console.log(CLIENT_ID_SECRET_WARNING);
111+
logger.warn(CLIENT_ID_SECRET_WARNING);
111112
}
112113
}
113114

@@ -140,7 +141,7 @@ export class IamTokenManager extends JwtTokenManager {
140141
rejectUnauthorized: !this.disableSslVerification,
141142
}
142143
};
143-
144+
144145
return this.requestWrapperInstance.sendRequest(parameters);
145146
}
146147
}

auth/token-managers/jwt-token-manager.ts

+10-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import extend = require('extend');
1818
import { OutgoingHttpHeaders } from 'http';
1919
import jwt = require('jsonwebtoken');
20+
import logger from '../../lib/logger';
2021
import { RequestWrapper } from '../../lib/request-wrapper';
2122

2223
function getCurrentTime(): number {
@@ -123,7 +124,9 @@ export class JwtTokenManager {
123124
* @returns {Promise}
124125
*/
125126
protected requestToken(): Promise<any> {
126-
const err = new Error('`requestToken` MUST be overridden by a subclass of JwtTokenManagerV1.');
127+
const errMsg = '`requestToken` MUST be overridden by a subclass of JwtTokenManagerV1.';
128+
const err = new Error(errMsg);
129+
logger.error(errMsg);
127130
return Promise.reject(err);
128131
}
129132

@@ -156,7 +159,9 @@ export class JwtTokenManager {
156159
const accessToken = tokenResponse[this.tokenName];
157160

158161
if (!accessToken) {
159-
throw new Error('Access token not present in response');
162+
const err = 'Access token not present in response';
163+
logger.error(err);
164+
throw new Error(err);
160165
}
161166

162167
this.expireTime = this.calculateTimeForNewToken(accessToken);
@@ -184,7 +189,9 @@ export class JwtTokenManager {
184189
const timeToLive = exp - iat;
185190
timeForNewToken = exp - (timeToLive * (1.0 - fractionOfTtl));
186191
} else {
187-
throw new Error('Access token recieved is not a valid JWT');
192+
const err = 'Access token recieved is not a valid JWT'
193+
logger.error(err);
194+
throw new Error(err);
188195
}
189196

190197
return timeForNewToken;

auth/utils/read-credentials-file.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import dotenv = require('dotenv');
22
import fs = require('fs');
33
import os = require('os');
44
import path = require('path');
5+
import logger from '../../lib/logger';
56

67
const filename: string = 'ibm-credentials.env';
78

@@ -36,6 +37,7 @@ export function readCredentialsFile() {
3637
filepathToUse = homeDir;
3738
} else {
3839
// file does not exist anywhere, will not be used
40+
logger.info('Credential file does not exist. Will not be used');
3941
return {};
4042
}
4143

lib/base-service.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import semver = require('semver');
2121
import vcapServices = require('vcap_services');
2222
import { AuthenticatorInterface, checkCredentials, readExternalSources } from '../auth';
2323
import { stripTrailingSlash } from './helper';
24+
import logger from './logger';
2425
import { RequestWrapper } from './request-wrapper';
2526

2627
export interface UserOptions {
@@ -65,9 +66,9 @@ export class BaseService {
6566
*/
6667
constructor(userOptions: UserOptions) {
6768
if (!(this instanceof BaseService)) {
68-
throw new Error(
69-
'the "new" keyword is required to create service instances'
70-
);
69+
const err = 'the "new" keyword is required to create service instances';
70+
logger.error(`Error creating an instance of BaseService: ${err}`);
71+
throw new Error(err);
7172
}
7273

7374
const _options = {} as BaseServiceOptions;
@@ -85,6 +86,7 @@ export class BaseService {
8586
// check serviceUrl for common user errors
8687
const credentialProblems = checkCredentials(options, ['serviceUrl']);
8788
if (credentialProblems) {
89+
logger.error(credentialProblems.message);
8890
throw credentialProblems;
8991
}
9092

lib/helper.ts

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import fileType = require('file-type');
1919
import { isReadable } from 'isstream';
2020
import { lookup } from 'mime-types';
2121
import { basename } from 'path';
22+
import logger from './logger';
2223

2324
export interface FileObject {
2425
value: NodeJS.ReadableStream | Buffer | string;
@@ -129,6 +130,7 @@ export function getMissingParams(
129130
* @returns {boolean} true if 'text' has html tags
130131
*/
131132
export function isHTML(text: string): boolean {
133+
logger.debug(`Determining if the text ${text} is HTML.`);
132134
return /<[a-z][\s\S]*>/i.test(text);
133135
}
134136

@@ -144,6 +146,7 @@ export function getFormat(
144146
formats: string[]
145147
): string {
146148
if (!formats || !params) {
149+
logger.debug(`No formats to parse in getFormat. Returning null`);
147150
return null;
148151
}
149152
for (const item of formats) {
@@ -152,6 +155,7 @@ export function getFormat(
152155
}
153156
}
154157

158+
logger.debug(`No formats to parse in getFormat. Returning null`);
155159
return null;
156160
}
157161

lib/logger.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import logger = require('debug');
2+
const debug = logger('ibm-cloud-sdk-core:debug');
3+
const error = logger('ibm-cloud-sdk-core:error');
4+
const info = logger('ibm-cloud-sdk-core:info');
5+
const verbose = logger('ibm-cloud-sdk-core:verbose');
6+
const warn = logger('ibm-cloud-sdk-core:warning');
7+
8+
// enable loggers if axios flag is set & mimic log levels severity
9+
if (process.env.NODE_DEBUG === 'axios') {
10+
debug.enabled = true;
11+
}
12+
if (debug.enabled) {
13+
verbose.enabled = true;
14+
}
15+
if (verbose.enabled) {
16+
info.enabled = true;
17+
}
18+
if (info.enabled) {
19+
warn.enabled = true;
20+
}
21+
if (warn.enabled) {
22+
error.enabled = true;
23+
}
24+
// export loggers;
25+
export default {
26+
debug,
27+
error,
28+
info,
29+
verbose,
30+
warn
31+
}

lib/request-wrapper.ts

+16-13
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import https = require('https');
2121
import querystring = require('querystring');
2222
import { PassThrough as readableStream } from 'stream';
2323
import { buildRequestFileObject, getMissingParams, isEmptyObject, isFileData, isFileWithMetadata } from './helper';
24+
import logger from './logger';
2425

2526
const isBrowser = typeof window === 'object';
2627
const globalTransactionId = 'x-global-transaction-id';
@@ -67,42 +68,42 @@ export class RequestWrapper {
6768
this.axiosInstance = axios.create(axiosConfig);
6869

6970
// set debug interceptors
70-
if(process.env.NODE_DEBUG === 'axios') {
71+
if(process.env.NODE_DEBUG === 'axios' || process.env.DEBUG) {
7172
this.axiosInstance.interceptors.request.use(config => {
72-
console.debug('Request:');
73+
logger.debug('Request:');
7374
try {
74-
console.debug(JSON.stringify(config, null, 2));
75+
logger.debug(JSON.stringify(config, null, 2));
7576
} catch {
76-
console.debug(config)
77+
logger.error(config)
7778
}
7879

7980
return config;
8081
}, error => {
81-
console.debug('Error:');
82+
logger.error('Error: ');
8283
try {
83-
console.debug(JSON.stringify(error, null, 2));
84+
logger.error(JSON.stringify(error, null, 2));
8485
} catch {
85-
console.debug(error);
86+
logger.error(error);
8687
}
8788

8889
return Promise.reject(error);
8990
});
9091

9192
this.axiosInstance.interceptors.response.use(response => {
92-
console.debug('Response:');
93+
logger.debug('Response:');
9394
try {
94-
console.debug(JSON.stringify(response, null, 2));
95+
logger.debug(JSON.stringify(response, null, 2));
9596
} catch {
96-
console.debug(response)
97+
logger.error(response);
9798
}
9899

99100
return response;
100101
}, error => {
101-
console.debug('Error:');
102+
logger.error('Error: ');
102103
try {
103-
console.debug(JSON.stringify(error, null, 2));
104+
logger.error(JSON.stringify(error, null, 2));
104105
} catch {
105-
console.debug(error);
106+
logger.error(error);
106107
}
107108

108109
return Promise.reject(error);
@@ -259,6 +260,7 @@ export class RequestWrapper {
259260
// ignore the error, use the object, and tack on a warning
260261
errorBody = axiosError.data;
261262
errorBody.warning = 'Body contains circular reference';
263+
logger.error(`Failed to stringify axiosError: ${e}`);
262264
}
263265

264266
error.body = errorBody;
@@ -385,5 +387,6 @@ function parseServiceErrorMessage(response: any): string | undefined {
385387
message = response.errorMessage;
386388
}
387389

390+
logger.info(`Parsing service error message: ${message}`);
388391
return message;
389392
}

0 commit comments

Comments
 (0)