1
- var util = require ( 'util' ) ,
2
- google = require ( 'googleapis' ) ;
3
-
4
- var glogging = google . logging ( "v2beta1" ) ;
5
-
6
- const loggingScopes = [
7
- //'https://www.googleapis.com/auth/logging.read',
8
- 'https://www.googleapis.com/auth/logging.write' ,
9
- //'https://www.googleapis.com/auth/logging.admin',
10
- //'https://www.googleapis.com/auth/cloud-platform'
11
- ] ;
1
+ var util = require ( 'util' ) ;
2
+ var logging = require ( '@google-cloud/logging' ) ;
3
+ var Writable = require ( 'stream' ) . Writable ;
12
4
13
5
const nameFromLevel = {
14
6
10 : 'trace' ,
@@ -24,98 +16,126 @@ const mapLevelToSeverity = {
24
16
info : 'INFO' ,
25
17
warn : 'WARNING' ,
26
18
error : 'ERROR' ,
27
- fatal : 'EMERGENCY'
28
- }
29
-
30
- function getNow ( ) {
31
- var d = new Date ( ) ;
32
- return JSON . parse ( JSON . stringify ( d ) . replace ( 'Z' , '000Z' ) ) ;
33
- }
19
+ fatal : 'ALERT'
20
+ } ;
34
21
22
+ util . inherits ( BunyanStackDriver , Writable ) ;
35
23
function BunyanStackDriver ( options , error ) {
24
+ Writable . call ( this , { objectMode : true } ) ;
25
+
36
26
options = options || { } ;
37
- if ( ! options . project ) {
38
- throw new Error ( 'Project cannot be null' ) ;
27
+
28
+ var gopts = { } ;
29
+
30
+ if ( options . projectId ) {
31
+ gopts . projectId = options . projectId ;
32
+ } else if ( process . env . GCLOUD_PROJECT ) {
33
+ // Noop, gcloud will pick this up.
39
34
} else {
35
+ throw new Error ( 'option "projectId" or env variable GCLOUD_PROJECT required' ) ;
36
+ }
40
37
41
- this . customFormatter = options . customFormatter ;
42
- this . project_id = options . project ;
43
- this . log_id = options . log_id || 'default' ;
44
- this . error = error || function ( ) { } ;
38
+ var logName = options . logName || 'default' ;
45
39
46
- // https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/LogEntry#LogSeverity
47
- if ( options . severity ) {
48
- this . severity = options . severity || 'DEFAULT' ;
49
- }
40
+ this . error = error || function ( ) { } ;
50
41
51
- // object(MonitoredResource)
52
- // https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource
53
- this . resource = options . resource || { type : 'global' } ;
42
+ // ms. GCP's default limit is 20 RPS.
43
+ this . writeInterval = 'writeInterval' in options ? options . writeInterval : 500 ;
54
44
45
+ // object(MonitoredResource)
46
+ // https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource
47
+ if ( options . resource ) {
48
+ if ( options . resource . type ) {
49
+ options . resource . labels = options . resource . labels || { } ; // required
50
+ this . resource = options . resource ;
51
+ } else {
52
+ throw new Error ( 'Property "type" required when specifying a resource' ) ;
53
+ }
54
+ } else {
55
+ this . resource = {
56
+ type : 'global' ,
57
+ labels : { }
58
+ } ;
55
59
}
56
60
57
- this . getLoggingClient = function ( callback ) {
58
- google . auth . fromJSON ( options . authJSON , function ( err , authClient ) {
59
- if ( err ) {
60
- return callback ( err ) ;
61
- }
62
- if ( authClient . createScopedRequired && authClient . createScopedRequired ( ) ) {
63
- authClient = authClient . createScoped ( loggingScopes ) ;
64
- }
65
- callback ( null , authClient ) ;
66
- } ) ;
61
+ // If not provided, gcloud will attempt automatic auth.
62
+ if ( options . keyFilename ) {
63
+ gopts . keyFilename = options . authJSON ;
67
64
}
65
+
66
+ var loggingClient = logging ( gopts ) ;
67
+
68
+ this . log = loggingClient . log ( logName ) ;
69
+
70
+ this . entryQueue = [ ] ;
68
71
}
69
72
70
- BunyanStackDriver . prototype . write = function write ( record , callback ) {
71
- var self = this ,
72
- levelName ,
73
- message ;
73
+ var once = true ;
74
74
75
+ BunyanStackDriver . prototype . _write = function write ( record , encoding , callback ) {
76
+ var timestamp ;
75
77
if ( typeof record === 'string' ) {
78
+ if ( once ) {
79
+ once = false ;
80
+ console . warn ( 'BunyanStackDriver: use "streams: [ type: \"raw\", stream: new BunyanStackDriver(...) ]" for better performance.' ) ;
81
+ }
76
82
record = JSON . parse ( record ) ;
83
+ timestamp = new Date ( record . time ) ;
84
+ } else {
85
+ timestamp = record . time ;
77
86
}
78
87
79
- levelName = nameFromLevel [ record . level ] ;
88
+ strictJSON ( record ) ;
80
89
81
- try {
90
+ var entry = this . log . entry ( this . resource , record ) ;
91
+
92
+ // There are no public APIs for this yet:
93
+ // https://github.com/GoogleCloudPlatform/gcloud-node/issues/1348
94
+ entry . timestamp = timestamp ;
95
+ entry . severity = mapLevelToSeverity [ nameFromLevel [ record . level ] ] || 'DEFAULT' ;
96
+
97
+ this . entryQueue . push ( entry ) ;
98
+
99
+ if ( ! this . writeQueued ) {
100
+ this . writeQueued = true ;
101
+ setTimeout ( this . _writeToServer . bind ( this ) , this . writeInterval ) ;
102
+ }
82
103
83
- if ( self . customFormatter ) {
84
- message = self . customFormatter ( record , levelName ) ;
85
- } else if ( typeof ( record ) == "string" ) {
86
- message = { text : util . format ( '[%s] %s' , levelName . toUpperCase ( ) , record . msg ) }
87
- } else {
88
- message = record ;
104
+ callback ( ) ;
105
+ } ;
106
+
107
+ /**
108
+ * Convert JS standard objects to strings, remove undefined. This is not
109
+ * cycle-safe.
110
+ * https://github.com/GoogleCloudPlatform/gcloud-node/issues/1354
111
+ */
112
+ function strictJSON ( o ) {
113
+ for ( var k in o ) {
114
+ var v = o [ k ] ;
115
+ if ( v instanceof Date || v instanceof Error || v instanceof RegExp ) {
116
+ o [ k ] = v . toJSON ( ) ;
117
+ } else if ( v === undefined ) {
118
+ delete o [ k ] ;
119
+ } else if ( typeof v === 'object' ) {
120
+ strictJSON ( v ) ;
89
121
}
90
- } catch ( err ) {
91
- return self . error ( err ) ;
92
122
}
123
+ }
93
124
94
- self . getLoggingClient ( function ( err , authClient ) {
95
- var params = {
96
- auth : authClient ,
97
- resource : {
98
- //logName: "projects/" + self.project_id + "/logs/" + self.log_id,
99
- //resource: self.resource,
100
- //labels: {},
101
- entries : [ {
102
- logName : "projects/" + self . project_id + "/logs/" + self . log_id ,
103
- resource : self . resource ,
104
- //timestamp: getNow(),
105
- severity : mapLevelToSeverity [ levelName ] || 'DEFAULT' ,
106
- //insertId,
107
- //httpRequest,
108
- //labels,
109
- //operation,
110
- [ ( message instanceof Object ) ?'jsonPayload' :'textPayload' ] : message
111
- } ] ,
112
- partialSuccess : true
113
- }
114
- } ;
125
+ BunyanStackDriver . prototype . _writeToServer = function ( ) {
126
+ var self = this ;
127
+
128
+ this . writeQueued = false ;
129
+
130
+ // Atomically get the entries to send and clear the queue
131
+ var entries = this . entryQueue . splice ( 0 ) ;
132
+
133
+ var options = {
134
+ partialSuccess : true
135
+ } ;
115
136
116
- glogging . entries . write ( params , function ( err , data ) {
117
- if ( err ) return self . error ( err ) ;
118
- } ) ;
137
+ this . log . write ( entries , options , function ( err , response ) {
138
+ if ( err ) return self . error ( err ) ;
119
139
} ) ;
120
140
} ;
121
141
0 commit comments