New Relic
애플리케이션 성능 모니터링 플랫폼 - APM, 인프라, 로그를 통합한 클라우드 기반 옵저버빌리티 솔루션
애플리케이션 성능 모니터링 플랫폼 - APM, 인프라, 로그를 통합한 클라우드 기반 옵저버빌리티 솔루션
New Relic은 2008년에 설립된 옵저버빌리티(Observability) 분야의 선두 기업으로, 애플리케이션 성능 모니터링(APM)에서 시작하여 현재는 메트릭, 이벤트, 로그, 트레이스(MELT)를 통합 분석하는 풀스택 옵저버빌리티 플랫폼으로 발전했습니다. SaaS 기반으로 제공되어 별도의 인프라 구축 없이 즉시 사용할 수 있으며, 월 100GB의 무료 데이터 수집 티어를 제공합니다.
New Relic의 핵심 기능은 크게 다섯 가지로 나뉩니다. APM은 코드 레벨의 성능 분석과 분산 트레이싱을 제공하고, Infrastructure는 호스트, 컨테이너, 클라우드 서비스의 리소스 모니터링을 담당합니다. Logs는 로그 수집 및 분석을, Browser와 Mobile은 실사용자 경험(RUM)을 추적합니다. 이 모든 데이터는 New Relic Query Language(NRQL)로 통합 분석할 수 있어 상관관계 파악이 용이합니다.
New Relic의 강점은 자동 계측(Auto-instrumentation)과 AI 기반 이상 탐지입니다. Java, Python, Node.js 등 주요 언어에 에이전트를 설치하면 코드 수정 없이 자동으로 트랜잭션, DB 쿼리, 외부 서비스 호출을 추적합니다. Applied Intelligence는 머신러닝으로 비정상 패턴을 감지하고, 연관된 알림을 그룹화하여 알림 피로도를 줄여줍니다. 또한 Service Map으로 마이크로서비스 간 의존성을 시각화합니다.
요금 체계는 사용량 기반(consumption-based)으로, 수집된 데이터 양에 따라 과금됩니다. 데이터 보존 기간, 사용자 유형(Full platform user vs Basic user)에 따라 비용이 달라지므로 사전에 예상 비용을 계산하는 것이 중요합니다. 오픈소스 텔레메트리 표준인 OpenTelemetry와의 호환성도 뛰어나 벤더 종속 없이 계측 데이터를 수집할 수 있습니다.
# requirements.txt에 추가
# newrelic
# newrelic.ini 설정 파일 생성
# newrelic-admin generate-config YOUR_LICENSE_KEY newrelic.ini
# newrelic.ini 주요 설정
"""
[newrelic]
license_key = YOUR_LICENSE_KEY
app_name = My Python Application
monitor_mode = true
log_level = info
ssl = true
high_security = false
transaction_tracer.enabled = true
transaction_tracer.transaction_threshold = apdex_f
transaction_tracer.record_sql = obfuscated
transaction_tracer.stack_trace_threshold = 0.5
error_collector.enabled = true
browser_monitoring.auto_instrument = true
"""
# Flask 애플리케이션 예제
import newrelic.agent
newrelic.agent.initialize('newrelic.ini')
from flask import Flask, jsonify
import time
app = Flask(__name__)
@app.route('/api/users/')
def get_user(user_id):
# 자동으로 트랜잭션 추적됨
# Custom attribute 추가
newrelic.agent.add_custom_attribute('user_id', user_id)
# DB 조회 시뮬레이션 (자동으로 DB 쿼리 추적됨)
user = fetch_user_from_db(user_id)
return jsonify(user)
@app.route('/api/process')
def process_data():
# Custom transaction 이름 지정
newrelic.agent.set_transaction_name('process_heavy_computation')
# Custom segment로 세부 측정
with newrelic.agent.FunctionTrace('heavy_computation'):
result = heavy_computation()
return jsonify({'result': result})
# 백그라운드 태스크 추적
@newrelic.agent.background_task()
def process_background_job(job_id):
newrelic.agent.add_custom_attribute('job_id', job_id)
# 작업 수행
pass
# 에러 수동 기록
def risky_operation():
try:
# 위험한 작업
pass
except Exception as e:
# New Relic에 에러 기록
newrelic.agent.notice_error()
raise
if __name__ == '__main__':
app.run(debug=True)
import newrelic.agent
from newrelic.api.application import application_instance
import requests
import time
class NewRelicMetrics:
def __init__(self, insert_key: str, account_id: str):
self.insert_key = insert_key
self.account_id = account_id
self.metric_api_url = "https://metric-api.newrelic.com/metric/v1"
self.event_api_url = f"https://insights-collector.newrelic.com/v1/accounts/{account_id}/events"
def record_custom_metric(self, name: str, value: float):
"""에이전트를 통한 커스텀 메트릭 기록"""
app = application_instance()
if app:
newrelic.agent.record_custom_metric(f'Custom/{name}', value, app)
def send_metric_api(self, metrics: list):
"""Metric API를 통한 직접 전송"""
payload = [{
"metrics": metrics
}]
headers = {
"Content-Type": "application/json",
"Api-Key": self.insert_key
}
response = requests.post(self.metric_api_url, json=payload, headers=headers)
return response.status_code == 202
def send_custom_event(self, event_type: str, attributes: dict):
"""커스텀 이벤트 전송"""
event = {
"eventType": event_type,
"timestamp": int(time.time()),
**attributes
}
headers = {
"Content-Type": "application/json",
"X-Insert-Key": self.insert_key
}
response = requests.post(self.event_api_url, json=[event], headers=headers)
return response.status_code == 200
# 사용 예시
metrics = NewRelicMetrics(
insert_key="NRII-xxxxxxxxxxxx",
account_id="1234567"
)
# 에이전트 메트릭 기록
metrics.record_custom_metric('OrderProcessingTime', 1.5)
# API로 gauge 메트릭 전송
metrics.send_metric_api([
{
"name": "custom.queue.depth",
"type": "gauge",
"value": 42,
"timestamp": int(time.time()),
"attributes": {
"queue.name": "orders",
"environment": "production"
}
}
])
# 커스텀 비즈니스 이벤트 전송
metrics.send_custom_event("OrderPlaced", {
"orderId": "ORD-12345",
"userId": "USR-67890",
"totalAmount": 99.99,
"itemCount": 3,
"paymentMethod": "credit_card"
})
-- 애플리케이션 응답 시간 추이
SELECT average(duration)
FROM Transaction
WHERE appName = 'My Python Application'
SINCE 1 hour ago
TIMESERIES 5 minutes
-- 느린 트랜잭션 Top 10
SELECT name, average(duration), count(*)
FROM Transaction
WHERE appName = 'My Python Application'
SINCE 1 day ago
FACET name
ORDER BY average(duration) DESC
LIMIT 10
-- 에러율 추이
SELECT percentage(count(*), WHERE error IS true) as 'Error Rate'
FROM Transaction
WHERE appName = 'My Python Application'
SINCE 24 hours ago
TIMESERIES 1 hour
-- DB 쿼리 성능 분석
SELECT average(databaseDuration), count(*)
FROM Transaction
WHERE appName = 'My Python Application'
AND databaseCallCount > 0
FACET databaseCallCount
SINCE 1 hour ago
-- 커스텀 이벤트 분석 (비즈니스 메트릭)
SELECT sum(totalAmount) as 'Total Revenue',
count(*) as 'Order Count',
average(totalAmount) as 'Average Order Value'
FROM OrderPlaced
SINCE 7 days ago
TIMESERIES 1 day
-- 인프라: 높은 CPU 사용률 호스트
SELECT average(cpuPercent)
FROM SystemSample
WHERE cpuPercent > 80
FACET hostname
SINCE 1 hour ago
-- 서비스 간 의존성 분석
SELECT count(*)
FROM Span
WHERE appName = 'My Python Application'
FACET externalHost
SINCE 1 hour ago
-- Apdex 점수 계산
SELECT apdex(duration, t: 0.5)
FROM Transaction
WHERE appName = 'My Python Application'
SINCE 1 day ago
TIMESERIES 1 hour
# providers.tf
terraform {
required_providers {
newrelic = {
source = "newrelic/newrelic"
version = "~> 3.0"
}
}
}
provider "newrelic" {
account_id = var.newrelic_account_id
api_key = var.newrelic_api_key
region = "US"
}
# variables.tf
variable "newrelic_account_id" {
type = string
description = "New Relic Account ID"
}
variable "newrelic_api_key" {
type = string
sensitive = true
description = "New Relic API Key"
}
variable "app_name" {
type = string
default = "My Python Application"
}
# alert_policy.tf
resource "newrelic_alert_policy" "app_policy" {
name = "${var.app_name} Alert Policy"
incident_preference = "PER_CONDITION_AND_TARGET"
}
# 응답 시간 알림
resource "newrelic_nrql_alert_condition" "high_response_time" {
policy_id = newrelic_alert_policy.app_policy.id
name = "High Response Time"
type = "static"
nrql {
query = "SELECT average(duration) FROM Transaction WHERE appName = '${var.app_name}'"
}
critical {
operator = "above"
threshold = 2.0
threshold_duration = 300
threshold_occurrences = "all"
}
warning {
operator = "above"
threshold = 1.0
threshold_duration = 300
threshold_occurrences = "all"
}
}
# 에러율 알림
resource "newrelic_nrql_alert_condition" "error_rate" {
policy_id = newrelic_alert_policy.app_policy.id
name = "High Error Rate"
type = "static"
nrql {
query = "SELECT percentage(count(*), WHERE error IS true) FROM Transaction WHERE appName = '${var.app_name}'"
}
critical {
operator = "above"
threshold = 5.0
threshold_duration = 300
threshold_occurrences = "at_least_once"
}
}
# Slack 알림 채널
resource "newrelic_notification_destination" "slack" {
name = "Slack Alerts"
type = "SLACK"
property {
key = "url"
value = var.slack_webhook_url
}
}
resource "newrelic_notification_channel" "slack_channel" {
name = "slack-alerts"
type = "SLACK"
destination_id = newrelic_notification_destination.slack.id
product = "IINT"
property {
key = "channelId"
value = var.slack_channel_id
}
}
# 알림 워크플로우
resource "newrelic_workflow" "alert_workflow" {
name = "${var.app_name} Alert Workflow"
muting_rules_handling = "NOTIFY_ALL_ISSUES"
issues_filter {
name = "policy-filter"
type = "FILTER"
predicate {
attribute = "labels.policyIds"
operator = "EXACTLY_MATCHES"
values = [newrelic_alert_policy.app_policy.id]
}
}
destination {
channel_id = newrelic_notification_channel.slack_channel.id
}
}