Java์—์„œ ๋ฉ”์‹œ์ง€ ๋กœ๊น…ํ•˜๊ธฐยถ

SLF4J API ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Java๋กœ ์ž‘์„ฑ๋œ ํ•จ์ˆ˜ ๋˜๋Š” ํ”„๋กœ์‹œ์ € ์ฒ˜๋ฆฌ๊ธฐ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋กœ๊น…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋กœ๊ทธ ํ•ญ๋ชฉ์„ ์ €์žฅํ•˜๋„๋ก ์ด๋ฒคํŠธ ํ…Œ์ด๋ธ”์„ ์„ค์ •ํ•˜๋ฉด Snowflake๊ฐ€ ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ์—์„œ ์ƒ์„ฑ๋œ ๋กœ๊ทธ ํ•ญ๋ชฉ์„ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

Snowflake์— ํฌํ•จ๋œ Snowflake ์›๊ฒฉ ๋ถ„์„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ํ•จ๊ป˜ ์ œ๊ณต๋˜๋Š” SLF4J API ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด ํ•จ์ˆ˜๋‚˜ ํ”„๋กœ์‹œ์ €๋ฅผ ๋งŒ๋“ค ๋•Œ PACKAGES ์ ˆ์— com.snowflake:telemetry:latest ๊ฐ’์„ ํฌํ•จํ•˜์‹ญ์‹œ์˜ค.

Maven์œผ๋กœ ์ฝ”๋“œ๋ฅผ ํŒจํ‚ค์ง•ํ•  ๋•Œ Telemetry ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํฌํ•จํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์›๊ฒฉ ๋ถ„์„ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก Java ๋ฐ Scala ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ฐธ๊ณ 

Snowflake ์›๊ฒฉ ๋ถ„์„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•จ์ˆ˜ ๋˜๋Š” ํ”„๋กœ์‹œ์ €์˜ ์‹คํ–‰ ํ™˜๊ฒฝ์— ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ Snowflake ์›๊ฒฉ ๋ถ„์„ ํŒจํ‚ค์ง€ ์ข…์†์„ฑ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ฐธ๊ณ 

SLF4J๋Š” FATAL ์ˆ˜์ค€์—์„œ ๋ฉ”์‹œ์ง€ ๋กœ๊น…์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Java ๋˜๋Š” Scala๋กœ ์ž‘์„ฑ๋œ ์ฒ˜๋ฆฌ๊ธฐ์˜ ๊ฒฝ์šฐ FATAL ์ˆ˜์ค€์€ ERROR ์ˆ˜์ค€์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด LOG_LEVEL ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ FATAL ๋กœ ์„ค์ •ํ•˜๋ฉด Java ๋˜๋Š” Scala ์ฒ˜๋ฆฌ๊ธฐ์˜ ERROR ์ˆ˜์ค€ ๋ฉ”์‹œ์ง€๊ฐ€ ์ˆ˜์ง‘๋ฉ๋‹ˆ๋‹ค.

Snowflake์—์„œ ๋กœ๊น… ์„ค์ • ๋ฐ ๋ฉ”์‹œ์ง€ ๊ฒ€์ƒ‰์— ๋Œ€ํ•œ ์ผ๋ฐ˜์ ์ธ ์ •๋ณด๋Š” ํ•จ์ˆ˜ ๋ฐ ํ”„๋กœ์‹œ์ €์˜ ๋ฉ”์‹œ์ง€ ๋กœ๊น…ํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ฝ”๋“œ์—์„œ ๋กœ๊น…ํ•˜๊ธฐ ์ „์— ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ง€์ • ํŠน์„ฑ ์ถ”๊ฐ€ํ•˜๊ธฐยถ

๋กœ๊ทธ ํ•ญ๋ชฉ์„ ์ƒ์„ฑํ•  ๋•Œ ํ‚ค-๊ฐ’ ํŽ˜์–ด๋กœ ์‚ฌ์šฉ์ž ๊ณ ์œ ์˜ ํŠน์„ฑ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Snowflake๋Š” ์ด๋Ÿฌํ•œ ์‚ฌ์šฉ์ž ์ง€์ • ํŠน์„ฑ์„ ์ด๋ฒคํŠธ ํ…Œ์ด๋ธ”์˜ RECORD_ATTRIBUTES ์—ด ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ง€์ • ํŠน์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด Logger.atInfo ๋ฐ Logger.atError ๊ฐ™์€ slf4j fluent API์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ ํ•ญ๋ชฉ์— ํ‚ค-๊ฐ’ ํŽ˜์–ด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ๊ฐ์€ ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” org.slf4j.spi.LoggingEventBuilder ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์ œ์˜ ์ฝ”๋“œ๋Š” ์ด๋ฒคํŠธ ํ…Œ์ด๋ธ”์˜ VALUE ์—ด์— โ€œํŠน์„ฑ ํฌํ•จ ๊ธฐ๋กโ€ ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ RECORD_ATTRIBUTES ์—ด์— ์‚ฌ์šฉ์ž ์ง€์ • ํŠน์„ฑ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

CREATE OR REPLACE PROCEDURE do_logging_java()
RETURNS VARCHAR
LANGUAGE JAVA
RUNTIME_VERSION = '11'
PACKAGES = ('com.snowflake:telemetry:latest','com.snowflake:snowpark:latest')
HANDLER = 'JavaLoggingHandler.doThings'
AS
$$
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import com.snowflake.snowpark_java.Session;

  public class JavaLoggingHandler {
    private static Logger logger = LoggerFactory.getLogger(JavaLoggingHandler.class);

    public String doThings(Session session) {
      logger.atInfo().addKeyValue("custom1", "value1").setMessage("Logging with attributes").log();
      return "SUCCESS";
    }
  }
$$;
Copy

์ด Logger.atInfo ํ˜ธ์ถœ์˜ ์ถœ๋ ฅ์€ ์ด๋ฒคํŠธ ํ…Œ์ด๋ธ”์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. RECORD_ATTRIBUTES ์—ด์—๋Š” Snowflake๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€ํ•˜๋Š” ํŠน์„ฑ์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

------------------------------------------------------------------
| VALUE                     | RECORD_ATTRIBUTES                  |
------------------------------------------------------------------
| "Logging with attributes" | {                                  |
|                           |   "custom1": "value1",             |
|                           |   "thread.name": "Thread-5"        |
|                           | }                                  |
------------------------------------------------------------------

Java ์˜ˆ์ œยถ

๋‹ค์Œ ์˜ˆ์ œ ๊ฐ€์ ธ์˜ค๊ธฐ์˜ ์ฝ”๋“œ๋Š” Snowflake ์›๊ฒฉ ๋ถ„์„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๋กœ๊ฑฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. INFO ์ˆ˜์ค€์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ๋กœ๊น…ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์˜ˆ์™ธ์— ๋Œ€ํ•œ ์˜ค๋ฅ˜๋ฅผ ๋กœ๊น…ํ•ฉ๋‹ˆ๋‹ค.

ํŠน์ • ์ˆ˜์ค€์—์„œ ๋กœ๊น…ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ SLF4J ๋ฉ”์„œ๋“œ ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

CREATE OR REPLACE PROCEDURE do_logging()
RETURNS VARCHAR
LANGUAGE JAVA
RUNTIME_VERSION = '11'
PACKAGES=('com.snowflake:snowpark:latest', 'com.snowflake:telemetry:latest')
HANDLER = 'JavaLoggingHandler.doThings'
AS
$$
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import com.snowflake.snowpark_java.Session;

  public class JavaLoggingHandler {
    private static Logger logger = LoggerFactory.getLogger(JavaLoggingHandler.class);

    public JavaLoggingHandler() {
      logger.info("Logging from within the constructor.");
    }

    public String doThings(Session session) {
      logger.info("Logging from method start.");

      try {
        throwException();
      } catch (Exception e) {
        logger.error("Logging an error: " + e.getMessage());
        return "ERROR";
      }
      return "SUCCESS";
    }

    // Simulate a thrown exception to catch.
    private void throwException() throws Exception {
      throw new Exception("Something went wrong.");
    }
  }
$$
;
Copy

์ด๋ฒคํŠธ ํ…Œ์ด๋ธ”์—์„œ SELECT ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์—ฌ ๋กœ๊ทธ ๋ฉ”์‹œ์ง€์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ ๋ณด๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

๋‹ค์Œ ์˜ˆ์ œ์˜ ์ฝ”๋“œ๋Š” ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๊ฐ€ ์ €์žฅ๋œ ์ด๋ฒคํŠธ ํ…Œ์ด๋ธ”์„ ์ฟผ๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด ์ฟผ๋ฆฌ๋Š” ์ฒ˜๋ฆฌ๊ธฐ ํด๋ž˜์Šค์—์„œ ๊ฐ ๋กœ๊ทธ ํ•ญ๋ชฉ์˜ ์‹ฌ๊ฐ๋„์™€ ๋ฉ”์‹œ์ง€์— ๋Œ€ํ•ด ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค.

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'] = 'JavaLoggingHandler'
  AND RECORD_TYPE = 'LOG';
Copy

์•ž์˜ ์˜ˆ์ œ์—์„œ๋Š” ๋‹ค์Œ ์ถœ๋ ฅ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

--------------------------------------------------------
| SEVERITY | MESSAGE                                   |
--------------------------------------------------------
| "INFO"   | "Logging from within the constructor."    |
--------------------------------------------------------
| "INFO"   | "Logging from method start."              |
--------------------------------------------------------
| "ERROR"  | "Logging an error: Something went wrong." |
--------------------------------------------------------