/**
 * Copyright 2018, OpenCensus Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ import { Exporter , ExporterBuffer , Span as OCSpan , SpanContext , } from '@opencensus/core' ; import { logger , Logger } from '@opencensus/core' ; import { auth as globalAuth , GoogleAuth , JWT } from 'google-auth-library' ; import { google } from 'googleapis' ; import { getDefaultResource } from './common-utils' ; import { createAttributes , createLinks , createTimeEvents , getResourceLabels , stringToTruncatableString , } from './stackdriver-cloudtrace-utils' ; import { AttributeValue , Span , SpansWithCredentials , StackdriverExporterOptions , } from './types' ; google . options ( { headers : { 'x-opencensus-outgoing-request' : 0x1 } } ) ; const cloudTrace = google . cloudtrace ( 'v2' ) ; let auth = globalAuth ; /** Format and sends span information to Stackdriver */ export class StackdriverTraceExporter implements Exporter { projectId : string ; exporterBuffer : ExporterBuffer ; logger : Logger ; failBuffer : SpanContext [ ] = [ ] ; private RESOURCE_LABELS : Promise < Record < string , AttributeValue > > ; constructor ( options ) { this . projectId = options . projectId ; this . logger = options . logger || logger . logger ( ) ; this . exporterBuffer = new ExporterBuffer ( this , options ) ; this . RESOURCE_LABELS = getResourceLabels ( getDefaultResource ( this . projectId ) ) ; if ( options . credentials ) { auth = new GoogleAuth ( { credentials : options . credentials , scopes : [ 'https://www.googleapis.com/auth/cloud-platform' ] , } ) ; } } param span onEndSpan ( span ) { if ( ! span . isRootSpan ( ) ) return ; this . exporterBuffer . addToBuffer ( span ) ; } /** Not used for this exporter */ onStartSpan ( span ) { } param spans async publish ( spans ) { const spanList = await this . translateSpan ( spans ) ; return this . authorize ( spanList ) . then ( ( spans ) => { return this . batchWriteSpans ( spans ) ; } ) . catch ( err => { for ( const span of spans ) { this . failBuffer . push ( span . spanContext ) ; } return err ; } ) ; } async translateSpan ( spans ) { const resourceLabel = await this . RESOURCE_LABELS ; const spanList = [ ] ; spans . forEach ( span => { spanList . push ( this . createSpan ( span , resourceLabel , span . numberOfChildren ) ) ; span . spans . forEach ( child => { spanList . push ( this . createSpan ( child , resourceLabel ) ) ; } ) ; } ) ; return spanList ; } private createSpan ( span , resourceLabels , numberOfChildren = 0 ) { const spanName = `projects/${ this . projectId }/traces/${ span . traceId }/spans/${ span . id }` ; const spanBuilder = { name : spanName , spanId : span . id , displayName : stringToTruncatableString ( span . name ) , startTime : span . startTime . toISOString ( ) , endTime : span . endTime . toISOString ( ) , attributes : createAttributes ( span . attributes , resourceLabels , span . droppedAttributesCount ) , timeEvents : createTimeEvents ( span . annotations , span . messageEvents , span . droppedAnnotationsCount , span . droppedMessageEventsCount ) , links : createLinks ( span . links , span . droppedLinksCount ) , status : { code : span . status . code } , sameProcessAsParentSpan : ! span . remoteParent , childSpanCount : numberOfChildren , stackTrace : undefined , } ; if ( span . parentSpanId ) { spanBuilder . parentSpanId = span . parentSpanId ; } if ( span . status . message && spanBuilder . status ) { spanBuilder . status . message = span . status . message ; } return spanBuilder ; } param spans private batchWriteSpans ( spans ) { return new Promise < string > ( ( resolve , reject ) => { cloudTrace . projects . traces . batchWrite ( spans , ( err ) => { if ( err ) { err . message = `batchWriteSpans error: ${ err . message }` ; this . logger . error ( err . message ) ; reject ( err ) ; } else { const successMsg = 'batchWriteSpans successfully' ; this . logger . debug ( successMsg ) ; resolve ( successMsg ) ; } } ) ; } ) ; } param stackdriverSpans private async authorize ( stackdriverSpans ) { try { const client = await auth . getClient ( ) ; return { name : `projects/${ this . projectId }` , resource : { spans : stackdriverSpans } , auth : client as JWT , } ; } catch ( err ) { err . message = `authorize error: ${ err . message }` ; this . logger . error ( err . message ) ; throw err ; } } }