Skip to content
This repository was archived by the owner on Mar 11, 2026. It is now read-only.

Commit 7f584bc

Browse files
fix: Regular error showing Total timeout of API google.logging.v2.LoggingServiceV2 exceeded 600000 milliseconds (#1225)
* fix: Regular error showing Total timeout of API google.logging.v2.LoggingServiceV2 exceeded 600000 milliseconds * Fix and add usage comments * Ammended a comment * Add a sample for callback usage * Add a sample to README for error handling * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Add a sample to readme-partials for error handling * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Fix a readme explanation * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Add sample link to README * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Comments fixes * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 536fb16 commit 7f584bc

5 files changed

Lines changed: 230 additions & 5 deletions

File tree

.readme-partials.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,58 @@ body: |-
7171
7272
If you already have a "raw" Http `request` object you can assign it to `entry.metadata.httpRequest` directly. More information about
7373
how the `request` is interpreted as raw can be found in the [code](https://github.com/googleapis/nodejs-logging/blob/15849160116a814ab71113138cb211c2e0c2d4b4/src/entry.ts#L224-L238).
74+
75+
## Error handling with logs written or deleted asynchronously
76+
77+
The `Log` class provide users the ability to write and delete logs asynchronously. However, there are cases when log entries
78+
cannot be written or deleted and error is thrown - if error is not handled properly, it could crash the application.
79+
One possible way to catch the error is to `await` the log write/delete calls and wrap it with `try/catch` like in example below:
80+
81+
```js
82+
// Write log entry and and catch any errors
83+
try {
84+
await log.write(entry);
85+
} catch (err) {
86+
console.log('Error is: ' + err);
87+
}
88+
```
89+
90+
However, awaiting for every `log.write` or `log.delete` calls may introduce delays which could be avoided by
91+
simply adding a callback like in the example below. This way the log entry can be queued for processing and code
92+
execution will continue without further delays. The callback will be called once the operation is complete:
93+
94+
```js
95+
// Asynchronously write the log entry and handle respone or any errors in provided callback
96+
log.write(entry, err => {
97+
if (err) {
98+
// The log entry was not written.
99+
console.log(err.message);
100+
} else {
101+
console.log('No error in write callback!');
102+
}
103+
});
104+
```
105+
106+
Adding a callback to every `log.write` or `log.delete` calls could be a burden, especially if code
107+
handling the error is always the same. For this purpose we introduced an ability to provide a default callback
108+
for `Log` class which could be set through `LogOptions` passed to `Log` constructor as in example below - this
109+
way you can define a global callback once for all `log.write` and `log.delete` calls and be able to handle errors:
110+
111+
```js
112+
const {Logging} = require('@google-cloud/logging');
113+
const logging = new Logging();
114+
115+
// Create options with default callback to be called on every write/delete response or error
116+
const options = {
117+
defaultWriteDeleteCallback: function (err) {
118+
if (err) {
119+
console.log('Error is: ' + err);
120+
} else {
121+
console.log('No error, all is good!');
122+
}
123+
},
124+
};
125+
126+
const log = logging.log('my-log', options);
127+
```
128+
See the full sample in `writeLogWithCallback` function [here](https://github.com/googleapis/nodejs-logging/blob/master/samples/logs.js).

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,61 @@ how to populate the Http request metadata for log entries.
162162
If you already have a "raw" Http `request` object you can assign it to `entry.metadata.httpRequest` directly. More information about
163163
how the `request` is interpreted as raw can be found in the [code](https://github.com/googleapis/nodejs-logging/blob/15849160116a814ab71113138cb211c2e0c2d4b4/src/entry.ts#L224-L238).
164164
165+
## Error handling with logs written or deleted asynchronously
166+
167+
The `Log` class provide users the ability to write and delete logs asynchronously. However, there are cases when log entries
168+
cannot be written or deleted and error is thrown - if error is not handled properly, it could crash the application.
169+
One possible way to catch the error is to `await` the log write/delete calls and wrap it with `try/catch` like in example below:
170+
171+
```js
172+
// Write log entry and and catch any errors
173+
try {
174+
await log.write(entry);
175+
} catch (err) {
176+
console.log('Error is: ' + err);
177+
}
178+
```
179+
180+
However, awaiting for every `log.write` or `log.delete` calls may introduce delays which could be avoided by
181+
simply adding a callback like in the example below. This way the log entry can be queued for processing and code
182+
execution will continue without further delays. The callback will be called once the operation is complete:
183+
184+
```js
185+
// Asynchronously write the log entry and handle respone or any errors in provided callback
186+
log.write(entry, err => {
187+
if (err) {
188+
// The log entry was not written.
189+
console.log(err.message);
190+
} else {
191+
console.log('No error in write callback!');
192+
}
193+
});
194+
```
195+
196+
Adding a callback to every `log.write` or `log.delete` calls could be a burden, especially if code
197+
handling the error is always the same. For this purpose we introduced an ability to provide a default callback
198+
for `Log` class which could be set through `LogOptions` passed to `Log` constructor as in example below - this
199+
way you can define a global callback once for all `log.write` and `log.delete` calls and be able to handle errors:
200+
201+
```js
202+
const {Logging} = require('@google-cloud/logging');
203+
const logging = new Logging();
204+
205+
// Create options with default callback to be called on every write/delete response or error
206+
const options = {
207+
defaultWriteDeleteCallback: function (err) {
208+
if (err) {
209+
console.log('Error is: ' + err);
210+
} else {
211+
console.log('No error, all is good!');
212+
}
213+
},
214+
};
215+
216+
const log = logging.log('my-log', options);
217+
```
218+
See the full sample in `writeLogWithCallback` function [here](https://github.com/googleapis/nodejs-logging/blob/master/samples/logs.js).
219+
165220
166221
## Samples
167222

samples/logs.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,49 @@ async function deleteLog(logName) {
209209
// [END logging_delete_log]
210210
}
211211

212+
async function writeLogWithCallback(logName) {
213+
const {Logging} = require('@google-cloud/logging');
214+
const logging = new Logging();
215+
216+
// Create options with default callback to be called on every write/delete response or error
217+
const options = {
218+
defaultWriteDeleteCallback: function (err) {
219+
if (err) {
220+
console.log('Error is: ' + err);
221+
} else {
222+
console.log('No error in default callback!');
223+
}
224+
},
225+
};
226+
227+
/**
228+
* TODO(developer): Uncomment the following line and replace with your values.
229+
*/
230+
// const logName = 'my-log';
231+
const log = logging.log(logName, options);
232+
233+
// A text log entry
234+
const text_entry = log.entry('Hello world!');
235+
236+
async function writeLogEntry() {
237+
// Asynchronously write the log entry and handle respone or any errors in provided callback
238+
log.write(text_entry, err => {
239+
if (err) {
240+
// The log entry was not written.
241+
console.log(err.message);
242+
} else {
243+
console.log('No error in write callback!');
244+
}
245+
});
246+
247+
// Let the logging library dispatch logs and handle response or any errors in defaultWriteDeleteCallback
248+
log.write(text_entry);
249+
250+
console.log(`Wrote to ${logName}`);
251+
}
252+
writeLogEntry();
253+
}
254+
212255
async function main() {
213256
require('yargs')
214257
.demand(1)
@@ -278,6 +321,14 @@ async function main() {
278321
'Write a JSON log entry.'
279322
)
280323
.example('node $0 delete my-log', 'Delete "my-log".')
324+
.command(
325+
'write-callback <logName>',
326+
'Writes a text log entry to the specified log and handles response or error in callback.',
327+
{},
328+
opts => {
329+
writeLogWithCallback(opts.logName);
330+
}
331+
)
281332
.wrap(120)
282333
.recommendCommands()
283334
.epilogue('For more information, see https://cloud.google.com/logging/docs')

src/log.ts

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export interface LogOptions {
6060
removeCircular?: boolean;
6161
maxEntrySize?: number; // see: https://cloud.google.com/logging/quotas
6262
jsonFieldsToTruncate?: string[];
63+
defaultWriteDeleteCallback?: ApiResponseCallback;
6364
}
6465

6566
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -89,12 +90,28 @@ export type DeleteCallback = ApiResponseCallback;
8990
* @param {number} [options.maxEntrySize] A max entry size
9091
* @param {string[]} [options.jsonFieldsToTruncate] A list of JSON properties at the given full path to be truncated.
9192
* Received values will be prepended to predefined list in the order received and duplicates discarded.
92-
*
93+
* @param {ApiResponseCallback} [options.defaultWriteDeleteCallback] A default global callback to be used for {@link Log#write}
94+
* and {@link Log#delete} APIs when {@link ApiResponseCallback} callback was not supplied by caller in function parameters.
95+
* Note that {@link LogOptions#defaultWriteDeleteCallback} is useful when {@link Log#write} and {@link Log#delete} APIs are called
96+
* without `await` and without callback added explicitly to every call - this way {@link LogOptions#defaultWriteDeleteCallback}
97+
* can serve as global callback handler, which for example could be used to catch all errors and eliminate crashes.
9398
* @example
9499
* ```
95-
* const {Logging} = require('@google-cloud/logging');
100+
* import {Logging} from '@google-cloud/logging';
101+
* import {LogOptions} from '@google-cloud/logging/build/src/log';
102+
* const options: LogOptions = {
103+
* maxEntrySize: 256,
104+
* jsonFieldsToTruncate: [
105+
* 'jsonPayload.fields.metadata.structValue.fields.custom.stringValue',
106+
* ],
107+
* defaultWriteDeleteCallback: (err: any) => {
108+
* if (err) {
109+
* console.log('Error: ' + err);
110+
* }
111+
* },
112+
* };
96113
* const logging = new Logging();
97-
* const log = logging.log('syslog');
114+
* const log = logging.log('syslog', options);
98115
* ```
99116
*/
100117
class Log implements LogSeverityFunctions {
@@ -104,6 +121,7 @@ class Log implements LogSeverityFunctions {
104121
logging: Logging;
105122
name: string;
106123
jsonFieldsToTruncate: string[];
124+
defaultWriteDeleteCallback?: ApiResponseCallback;
107125

108126
constructor(logging: Logging, name: string, options?: LogOptions) {
109127
options = options || {};
@@ -145,6 +163,13 @@ class Log implements LogSeverityFunctions {
145163
this.jsonFieldsToTruncate
146164
);
147165
}
166+
167+
/**
168+
* The default callback for {@link Log#write} and {@link Log#delete} APIs
169+
* is going to be used only when {@link LogOptions#defaultWriteDeleteCallback}
170+
* was set by user and only for APIs which does not accept a callback as parameter
171+
*/
172+
this.defaultWriteDeleteCallback = options.defaultWriteDeleteCallback;
148173
}
149174

150175
/**
@@ -347,7 +372,8 @@ class Log implements LogSeverityFunctions {
347372
};
348373
return this.logging.loggingService.deleteLog(
349374
reqOpts,
350-
gaxOptions! as CallOptions
375+
gaxOptions! as CallOptions,
376+
this.defaultWriteDeleteCallback
351377
);
352378
}
353379

@@ -953,7 +979,8 @@ class Log implements LogSeverityFunctions {
953979
delete reqOpts.gaxOptions;
954980
return this.logging.loggingService.writeLogEntries(
955981
reqOpts,
956-
options.gaxOptions
982+
options.gaxOptions,
983+
this.defaultWriteDeleteCallback
957984
);
958985
}
959986

test/log.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,27 @@ describe('Log', () => {
169169
{
170170
logName: log.formattedName_,
171171
},
172+
undefined,
172173
undefined
173174
)
174175
);
175176
});
176177

178+
it('should execute global callback for delete', async () => {
179+
log.defaultWriteDeleteCallback = () => {};
180+
await log.delete();
181+
assert(
182+
log.logging.loggingService.deleteLog.calledWithExactly(
183+
{
184+
logName: log.formattedName_,
185+
},
186+
undefined,
187+
log.defaultWriteDeleteCallback
188+
)
189+
);
190+
log.defaultWriteDeleteCallback = undefined;
191+
});
192+
177193
it('should accept gaxOptions', async () => {
178194
await log.delete({});
179195
assert(
@@ -368,6 +384,7 @@ describe('Log', () => {
368384
},
369385
},
370386
},
387+
undefined,
371388
undefined
372389
)
373390
);
@@ -432,6 +449,7 @@ describe('Log', () => {
432449
entries: ENTRIES,
433450
resource: EXPECTED_RESOURCE,
434451
},
452+
undefined,
435453
undefined
436454
)
437455
);
@@ -446,11 +464,29 @@ describe('Log', () => {
446464
entries: ENTRIES,
447465
resource: FAKE_RESOURCE,
448466
},
467+
undefined,
449468
undefined
450469
)
451470
);
452471
});
453472

473+
it('should call gax write method with global callback', async () => {
474+
log.defaultWriteDeleteCallback = () => {};
475+
await log.write(ENTRIES, OPTIONS);
476+
assert(
477+
log.logging.loggingService.writeLogEntries.calledOnceWithExactly(
478+
{
479+
logName: log.formattedName_,
480+
entries: ENTRIES,
481+
resource: FAKE_RESOURCE,
482+
},
483+
undefined,
484+
log.defaultWriteDeleteCallback
485+
)
486+
);
487+
log.defaultWriteDeleteCallback = undefined;
488+
});
489+
454490
it('should decorate the entries', async () => {
455491
decorateEntriesStub.resetBehavior();
456492
decorateEntriesStub.returns('decorated entries');
@@ -498,6 +534,7 @@ describe('Log', () => {
498534
assert(
499535
log.logging.loggingService.writeLogEntries.calledOnceWithExactly(
500536
sinon.match.object,
537+
undefined,
501538
undefined
502539
)
503540
);

0 commit comments

Comments
 (0)