Adding support for the telemetry API
To use Telemetry methods, you must make the open source Snowflake telemetry package (https://central.sonatype.com/artifact/com.snowflake/telemetry ) available to your handler code.
In the PACKAGES clause in your CREATE PROCEDURE or CREATE FUNCTION statement, include the com.snowflake:telemetry package. The
PACKAGES clause makes the included Snowflake Telemetry API available to your code.
Code in the following example uses the PACKAGES clause to reference the Telemetry library as well as the Snowpark library (which is
required for stored procedures written in Java – for more information, see Writing Java handlers for stored procedures created with SQL ).
Copy code Expand code block CREATE OR REPLACE PROCEDURE myproc( . . . )
RETURNS . . .
LANGUAGE JAVA
. . .
PACKAGES = ( 'com.snowflake:snowpark:latest' , 'com.snowflake:telemetry:latest' )
. . .
Import the com.snowflake.telemetry package in your Java handler code.
Copy code Expand code block import com.snowflake.telemetry.Telemetry;
Adding trace events
You can add trace events by calling the Telemetry.addEvent method, passing a name for the event. You can also optionally associate
attributes – key-value pairs – with an event.
The addEvent method has the following signatures:
Copy code Expand code block public static void addEvent (String name)
public static void addEvent (String name, Attributes attributes)
Code in the following example adds an event called testEvent, associating with the event two attributes: key and result.
Copy code Expand code block
Telemetry.addEvent("testEvent" );
Attributes eventAttributes = Attributes.of(
AttributeKey.stringKey("key" ), "run" ,
AttributeKey.longKey("result" ), Long.valueOf(123 ));
Telemetry.addEvent("testEventWithAttributes" , eventAttributes);
Adding these events results in two rows in the event table, each with a different value in the RECORD column:
Copy code Expand code block
Copy code Expand code block {
"name" : "testEventWithAttributes"
}
The testEventWithAttributes event row includes the following attributes in the row’s RECORD_ATTRIBUTES column:
Copy code Expand code block {
"key" : "run" ,
"result" : 123
}
Adding span attributes
You can set attributes – key-value pairs – associated with spans by calling the Telemetry.setSpanAttribute method.
The setSpanAttribute method has the following signatures:
Copy code Expand code block public static void setSpanAttribute (String key, boolean value)
public static void setSpanAttribute (String key, long value)
public static void setSpanAttribute (String key, double value)
public static void setSpanAttribute (String key, String value)
For details on spans, see How Snowflake represents trace events .
Code in the following example creates four attributes and sets their values:
Copy code Expand code block
Telemetry.setSpanAttribute("example.boolean" , true );
Telemetry.setSpanAttribute("example.long" , 2L );
Telemetry.setSpanAttribute("example.double" , 2.5 );
Telemetry.setSpanAttribute("example.string" , "testAttribute" );
Setting these attributes results in the following in the event table’s RECORD_ATTRIBUTES column:
Copy code Expand code block {
"example.boolean" : true ,
"example.long" : 2 ,
"example.double" : 2.5 ,
"example.string" : "testAttribute"
}
Adding custom spans
You can add custom spans that are separate from the default span created by Snowflake. For details on custom spans, see
Adding custom spans to a trace .
Code in the following example uses the OpenTelemetry API (https://javadoc.io/doc/io.opentelemetry/opentelemetry-api/latest/index.html ) and OpenTelemetry context propagation API (https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-context-prop/latest/index.html ) to create a new my.span
span. It then adds an event with attributes to the new span. Finally, the code ends the span to have the span’s event data
captured in the event table. If the code doesn’t call the Span.end method, data is not captured in the event table.
Copy code Expand code block CREATE OR REPLACE FUNCTION customSpansJavaExample () RETURNS STRING
LANGUAGE JAVA
PACKAGES = ('com.snowflake:telemetry:latest' )
HANDLER = 'MyJavaClass.run'
as
$$
import com.snowflake.telemetry.Telemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
class MyJavaClass {
public static String run () {
Tracer tracer = GlobalOpenTelemetry.getTracerProvider().get("my.tracer" );
Span span = tracer.spanBuilder("my.span" ).startSpan();
try (Scope scope = span.makeCurrent()) {
Attributes eventAttributes = Attributes.of(
AttributeKey.stringKey("key" ), "run" ,
AttributeKey.longKey("result" ), Long.valueOf(123 ));
span.addEvent("testEventWithAttributes" , eventAttributes);
span.setAttribute("testAttribute" , "value" );
} finally {
span.end();
}
return "success" ;
}
}
$$;
Java examples
Stored procedure example
Copy code Expand code block CREATE OR REPLACE PROCEDURE do_tracing ()
RETURNS STRING
LANGUAGE JAVA
RUNTIME_VERSION = '11'
PACKAGES=('com.snowflake:snowpark:latest' , 'com.snowflake:telemetry:latest' )
HANDLER = 'ProcedureHandler.run'
AS
$$
import com.snowflake.snowpark_java.Session;
import com.snowflake.telemetry.Telemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
public class ProcedureHandler {
public String run (Session session) {
Telemetry.setSpanAttribute("example.proc.do_tracing" , "begin" );
Telemetry.addEvent("run_method_start" );
Attributes eventAttributes = Attributes.of(
AttributeKey.stringKey("example.method.name" ), "run" ,
AttributeKey.longKey("example.long" ), Long.valueOf(123 ));
Telemetry.addEvent("event_with_attributes" , eventAttributes);
Telemetry.setSpanAttribute("example.proc.do_tracing" , "complete" );
return "SUCCESS" ;
}
}
$$;
UDF example
Copy code Expand code block CREATE OR REPLACE FUNCTION add_two_numbers (A FLOAT, B FLOAT) RETURNS FLOAT
LANGUAGE JAVA
PACKAGES=('com.snowflake:telemetry:latest' )
HANDLER = 'ScalarFunctionHandler.run'
AS
$$
import com.snowflake.telemetry.Telemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
class ScalarFunctionHandler {
public static Double run (Double d0, Double d1) {
Telemetry.setSpanAttribute("example.func.add_two_numbers" , "begin" );
Telemetry.addEvent("run_method_start" );
Attributes eventAttributes = Attributes.of(
AttributeKey.stringKey("example.method.name" ), "run" ,
AttributeKey.longKey("example.long" ), Long.valueOf(123 ));
Telemetry.addEvent("event_with_attributes" , eventAttributes);
Double response = d0 == null || d1 == null ? null : (d0 + d1);
Telemetry.setSpanAttribute("example.func.add_two_numbers.response" , response);
Telemetry.setSpanAttribute("example.func.add_two_numbers" , "complete" );
return response;
}
}
$$;
UDTF example
Copy code Expand code block CREATE OR REPLACE FUNCTION digits_of_number (x int )
RETURNS TABLE (result INT)
LANGUAGE JAVA
PACKAGES = ('com.snowflake:telemetry:latest' )
HANDLER = 'TableFunctionHandler'
AS
$$
import com.snowflake.telemetry.Telemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import java.util.stream.Stream;
public class TableFunctionHandler {
public TableFunctionHandler () {
Telemetry.setSpanAttribute("example.func.digits_of_number" , "begin" );
}
static class OutputRow {
public int result;
public OutputRow (int result) {
this .result = result;
}
}
public static Class getOutputClass () {
return OutputRow.class;
}
public Stream<OutputRow> process (int input) {
Attributes eventAttributes = Attributes.of(
AttributeKey.longKey("example.received.value" ), Long.valueOf(input));
Telemetry.addEvent("digits_of_number" , eventAttributes);
Stream.Builder<OutputRow> stream = Stream.builder();
while (input > 0 ) {
stream.add(new OutputRow (input %10 ));
input /= 10 ;
}
Telemetry.setSpanAttribute("example.func.digits_of_number" , "complete" );
return stream.build();
}
}
$$;