Logging messages in Scala¶
You can log messages from a function or procedure handler written in Scala by using the SLF4J API. When you’ve set up an event table to store log entries, Snowflake stores log entries generated by your handler code in the table.
You can use the SLF4J API included with the Snowflake Telemetry library included on Snowflake. To do so, include the following
value in the PACKAGES clause when you create the function or procedure: com.snowflake:telemetry:latest
.
For information on including the Telemetry library when packaging your code with Maven, see Setting up your Java and Scala environment to use the Telemetry class.
Note
Using the Snowflake Telemetry Library adds other libraries to your function or procedure’s execution environment. For more information, see Snowflake telemetry package dependencies.
Note
SLF4J does not support logging messages at the FATAL
level. For handlers written in Java or Scala, the FATAL
level is
treated as the ERROR
level.
For example, if you set the LOG_LEVEL
parameter to FATAL
, ERROR
-level messages from a Java or Scala
handler are ingested.
For general information about setting up logging and retrieving messages in Snowflake, see Logging messages from functions and procedures.
Before logging from code, you must:
Set up an event table to collect messages logged from handler code.
For more information, see Event table overview.
Be sure you have the logging level set so that the messages you want are stored in the event table.
For more information, see Setting levels for logging, metrics, and tracing.
Adding custom attributes¶
When you create a log entry, you can add your own attributes in key-value pairs. Snowflake saves these custom attributes to the event table’s RECORD_ATTRIBUTES column.
To add custom attributes, call methods of the slf4j fluent API, such as Logger.atInfo
and Logger.atError
. Use these
methods to set key-value pairs in the log entry. Each returns an org.slf4j.spi.LoggingEventBuilder, which you can use to set the
log message.
Code in the following example logs a message “Logging with attributes” to the event table’s VALUE column. It also adds a custom attribute to the RECORD_ATTRIBUTES column.
CREATE OR REPLACE PROCEDURE do_logging_scala()
RETURNS VARCHAR
LANGUAGE SCALA
RUNTIME_VERSION = '2.12'
PACKAGES=('com.snowflake:telemetry:latest', 'com.snowflake:snowpark:latest')
HANDLER = 'ScalaLoggingHandler.doThings'
AS
$$
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import com.snowflake.snowpark.Session
class ScalaLoggingHandler {
private val logger: Logger = LoggerFactory.getLogger(getClass)
def doThings(session: Session): String = {
logger.atInfo().addKeyValue("custom1", "value1").setMessage("Logging with attributes").log();
return "SUCCESS"
}
}
$$;
Output of this Logger.atInfo
call appears in the event table as follows. Note that the RECORD_ATTRIBUTES column will include
attributes that Snowflake adds automatically.
------------------------------------------------------------------
| VALUE | RECORD_ATTRIBUTES |
------------------------------------------------------------------
| "Logging with attributes" | { |
| | "custom1": "value1", |
| | "thread.name": "Thread-5" |
| | } |
------------------------------------------------------------------
Scala example¶
Code in the following example imports references the Snowflake Telemetry library and from it gets a logger. It logs a message at the
INFO
level. It also logs an error for an exception.
For more information about the methods you can use to log at specific levels, see SLF4J methods.
CREATE OR REPLACE PROCEDURE do_logging()
RETURNS VARCHAR
LANGUAGE SCALA
RUNTIME_VERSION = '2.12'
PACKAGES=('com.snowflake:snowpark:latest', 'com.snowflake:telemetry:latest')
HANDLER = 'ScalaLoggingHandler.doThings'
AS
$$
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import com.snowflake.snowpark.Session
class ScalaLoggingHandler {
private val logger: Logger = LoggerFactory.getLogger(getClass)
logger.info("Logging from within the Scala constructor.")
def doThings(session: Session): String = {
logger.info("Logging from Scala method start.")
try {
throwException
} catch {
case e: Exception => logger.error("Logging an error from Scala handler: " + e.getMessage())
return "ERROR"
}
return "SUCCESS"
}
// Simulate a thrown exception to catch.
@throws(classOf[Exception])
private def throwException = {
throw new Exception("Something went wrong.")
}
}
$$
;
You can access log messages by executing a SELECT command on the event table. For more information, see Viewing log messages.
Code in the following example queries the event table where the log messages are stored. The query reports on the severity and message of each log entry from the handler class.
SET event_table_name='my_db.public.my_event_table';
SELECT
RECORD['severity_text'] AS SEVERITY,
VALUE AS MESSAGE
FROM
IDENTIFIER($event_table_name)
WHERE
SCOPE['name'] = 'ScalaLoggingHandler'
AND RECORD_TYPE = 'LOG';
The preceding example generates the following output.
---------------------------------------------------------------------------
| SEVERITY | MESSAGE |
---------------------------------------------------------------------------
| "INFO" | "Logging from within the Scala constructor." |
---------------------------------------------------------------------------
| "INFO" | "Logging from Scala method start." |
---------------------------------------------------------------------------
| "ERROR" | "Logging an error from Scala handler: Something went wrong." |
---------------------------------------------------------------------------