New Relicカスタムイベントと属性 - ビジネスメトリクスの計測と分析

New Relicの標準的な監視機能だけでは捉えきれない、ビジネス固有の重要な指標があります。カスタムイベントとカスタム属性を活用することで、技術的なメトリクスとビジネス価値を統合した、より意味のある監視環境を構築できます。この記事では、カスタムデータの設計から実装、分析まで、実践的なアプローチを詳しく解説していきます。

カスタムイベントとカスタム属性の基本概念

カスタムイベントは、アプリケーション内で発生する特定のビジネスイベントを記録するための仕組みです。ユーザーの購入完了、プレミアム機能の利用、重要なワークフローの完了など、ビジネス的に意味のある出来事を構造化データとして保存できます。

標準のAPM データとは異なり、カスタムイベントは完全に自由な形式で定義できます。イベント名、タイムスタンプ、そして任意の数の属性(キー・バリューペア)で構成され、ビジネスの特性に応じて柔軟にデータ構造を設計できます。

カスタム属性は、既存のNew Relicイベント(TransactionやPageViewなど)に追加情報を付与する機能です。例えば、HTTPリクエストに対してユーザーの種別(プレミアム/スタンダード)や地理的情報、A/Bテストのバリエーションなどの情報を追加できます。

これらの機能により、技術的なパフォーマンス指標とビジネス文脈を関連付けた分析が可能になります。「プレミアムユーザーの応答時間は一般ユーザーより速いか」「特定の地域でエラー率が高いか」といった、ビジネス価値に直結する質問に答えることができます。

カスタムイベントには重要な制限があります。1つのイベントには最大255個の属性、各属性名は255文字以内、文字列値は4,096文字以内という制約があります。また、1分間に送信できるカスタムイベント数は最大10万件までとなっており、これを超過すると一部のイベントが破棄される可能性があります。

カスタムイベントの実装方法

カスタムイベントの実装は、使用するプログラミング言語とNew Relic エージェントによって異なりますが、基本的なパターンは共通しています。以下に主要な言語での実装例を示します。

Node.js での実装例

javascript
const newrelic = require('newrelic');

// 購入完了イベントの記録
function recordPurchaseEvent(orderData) {
  newrelic.recordCustomEvent('PurchaseCompleted', {
    orderId: orderData.id,
    amount: orderData.totalAmount,
    currency: orderData.currency,
    paymentMethod: orderData.paymentMethod,
    customerTier: orderData.customer.tier,
    productCategory: orderData.items[0].category,
    shippingMethod: orderData.shipping.method,
    promocodeUsed: orderData.promocode !== null,
    itemCount: orderData.items.length
  });
}

// ユーザーエンゲージメントイベント
function recordUserEngagement(actionData) {
  newrelic.recordCustomEvent('UserEngagement', {
    userId: actionData.userId,
    action: actionData.action,
    featureName: actionData.feature,
    sessionDuration: actionData.sessionTime,
    deviceType: actionData.device.type,
    browserVersion: actionData.browser.version
  });
}

Python での実装例

python
import newrelic.agent

@newrelic.agent.background_task()
def process_subscription_renewal(subscription_data):
    # カスタムイベントの記録
    newrelic.agent.record_custom_event('SubscriptionRenewed', {
        'subscription_id': subscription_data['id'],
        'plan_type': subscription_data['plan'],
        'renewal_amount': subscription_data['amount'],
        'customer_lifetime_value': subscription_data['customer_ltv'],
        'renewal_method': subscription_data['method'],
        'previous_plan': subscription_data.get('previous_plan'),
        'upgrade': subscription_data['amount'] > subscription_data.get('previous_amount', 0)
    })

Java での実装例

java
import com.newrelic.api.agent.NewRelic;
import java.util.HashMap;
import java.util.Map;

public class BusinessMetricsCollector {
    
    public void recordFeatureUsage(String userId, String featureName, boolean isPremium) {
        Map<String, Object> eventAttributes = new HashMap<>();
        eventAttributes.put("userId", userId);
        eventAttributes.put("featureName", featureName);
        eventAttributes.put("isPremiumUser", isPremium);
        eventAttributes.put("timestamp", System.currentTimeMillis());
        
        NewRelic.recordCustomEvent("FeatureUsage", eventAttributes);
    }
    
    public void recordAPIUsage(String endpoint, int responseTime, String clientType) {
        Map<String, Object> eventAttributes = new HashMap<>();
        eventAttributes.put("endpoint", endpoint);
        eventAttributes.put("responseTime", responseTime);
        eventAttributes.put("clientType", clientType);
        
        NewRelic.recordCustomEvent("APIUsage", eventAttributes);
    }
}

カスタム属性の追加と活用

カスタム属性は、既存のトランザクションやページビューに追加コンテキストを提供します。ユーザー認証後にユーザー情報を属性として追加したり、リクエスト処理中に計算されたビジネス指標を記録したりできます。

javascript
// Node.js でのカスタム属性追加
const newrelic = require('newrelic');

// ユーザー認証時の属性追加
function addUserAttributes(user) {
  newrelic.addCustomAttribute('user.id', user.id);
  newrelic.addCustomAttribute('user.tier', user.subscriptionTier);
  newrelic.addCustomAttribute('user.region', user.region);
  newrelic.addCustomAttribute('user.signupDate', user.createdAt);
  newrelic.addCustomAttribute('user.lastLoginDays', 
    Math.floor((Date.now() - user.lastLogin) / (1000 * 60 * 60 * 24)));
}

// リクエスト処理中の動的属性追加
function processRequest(req, res, next) {
  // A/Bテストバリエーション
  const abTestVariant = determineABTestVariant(req.user);
  newrelic.addCustomAttribute('ab.test.variant', abTestVariant);
  
  // 機能フラグの状態
  const featureFlags = getFeatureFlags(req.user);
  Object.keys(featureFlags).forEach(flag => {
    newrelic.addCustomAttribute(`feature.${flag}`, featureFlags[flag]);
  });
  
  next();
}

データモデリングの設計原則

効果的なカスタムデータ設計には、いくつかの重要な原則があります。これらに従うことで、分析しやすく、パフォーマンスに影響を与えない実装が可能になります。

属性の命名規則を統一することが重要です。ドット記法(user.tier、order.amount)を使用して階層構造を表現し、キャメルケースまたはスネークケースで一貫性を保ちます。

javascript
// 良い命名例
{
  'user.id': 'user123',
  'user.tier': 'premium',
  'order.amount': 150.99,
  'order.currency': 'USD',
  'payment.method': 'credit_card',
  'shipping.method': 'express',
  'product.category': 'electronics'
}

// 避けるべき命名例
{
  'userId': 'user123',
  'UserTier': 'premium',
  'order_amount': 150.99,
  'Currency': 'USD'
}

カーディナリティの管理も重要な考慮事項です。ユニークな値の数が多すぎる属性(個別のユーザーIDなど)は、インデックス効率や集計パフォーマンスに影響を与える可能性があります。

javascript
// 高カーディナリティ(注意が必要)
newrelic.addCustomAttribute('user.id', userId); // 数百万の異なる値

// 適切なカーディナリティ
newrelic.addCustomAttribute('user.tier', userTier); // premium, standard, trial など

// カーディナリティを下げる工夫
newrelic.addCustomAttribute('user.signupMonth', 
  new Date(user.signupDate).toISOString().substr(0, 7)); // 2024-01 形式

ビジネスメトリクスの分析例

カスタムイベントとカスタム属性を活用した実際の分析例を見てみましょう。

売上パフォーマンス分析

sql
-- 決済方法別の平均注文金額
SELECT average(amount) as 'Average Order Value',
       count(*) as 'Order Count',
       sum(amount) as 'Total Revenue'
FROM PurchaseCompleted
WHERE currency = 'USD'
SINCE 30 days ago
FACET paymentMethod
ORDER BY sum(amount) DESC

-- プレミアムユーザーの購入パターン
SELECT count(*) as 'Purchases',
       average(amount) as 'Average Amount',
       percentage(count(*), WHERE promocodeUsed = true) as 'Promo Usage Rate'
FROM PurchaseCompleted
FACET customerTier
SINCE 7 days ago

ユーザーエンゲージメント分析

sql
-- 機能別のユーザーエンゲージメント
SELECT count(*) as 'Usage Count',
       uniqueCount(userId) as 'Unique Users'
FROM UserEngagement
FACET featureName
SINCE 1 week ago
ORDER BY count(*) DESC

-- デバイスタイプ別のセッション時間
SELECT average(sessionDuration) as 'Average Session Duration',
       percentile(sessionDuration, 50, 90, 95) as 'Session Duration Percentiles'
FROM UserEngagement
FACET deviceType
SINCE 24 hours ago

A/Bテスト結果分析

sql
-- A/Bテストバリエーション別のコンバージョン率
SELECT uniqueCount(user.id) as 'Unique Users',
       filter(uniqueCount(user.id), WHERE eventType = 'PurchaseCompleted') as 'Conversions',
       percentage(uniqueCount(user.id), WHERE eventType = 'PurchaseCompleted') as 'Conversion Rate'
FROM Transaction
WHERE ab.test.variant IS NOT NULL
SINCE 14 days ago
FACET ab.test.variant

パフォーマンスとベストプラクティス

カスタムデータの実装においては、アプリケーションのパフォーマンスへの影響を最小限に抑えることが重要です。

非同期処理を活用して、カスタムイベントの送信がアプリケーションの応答時間に影響しないようにします。

javascript
// 非同期でのカスタムイベント記録
async function processOrder(orderData) {
  try {
    // メインの処理
    const result = await completeOrder(orderData);
    
    // カスタムイベントは非同期で記録
    setImmediate(() => {
      recordPurchaseEvent(orderData);
    });
    
    return result;
  } catch (error) {
    // エラーイベントも記録
    setImmediate(() => {
      newrelic.recordCustomEvent('OrderProcessingError', {
        orderId: orderData.id,
        errorType: error.constructor.name,
        errorMessage: error.message.substring(0, 100)
      });
    });
    throw error;
  }
}

データ量の制限を意識し、必要最小限の情報のみを記録します。文字列属性は適切な長さに制限し、不要な詳細情報は除外します。

javascript
// 適切なデータサイズ管理
function sanitizeCustomData(data) {
  return {
    userId: data.userId,
    action: data.action.substring(0, 50), // 文字列長制限
    amount: Math.round(data.amount * 100) / 100, // 小数点精度制限
    timestamp: Date.now(),
    // 不要な大きなオブジェクトは除外
    // rawData: data.rawData // これは含めない
  };
}

まとめ

カスタムイベントとカスタム属性は、New Relicの監視機能を大幅に拡張し、ビジネス価値に直結するインサイトを提供する強力な機能です。適切な設計と実装により、技術的な監視とビジネス分析を統合した、包括的な観測可能性を実現できます。

重要なのは、技術的な実装だけでなく、ビジネス要求と分析目的を明確にし、長期的に維持可能なデータ構造を設計することです。継続的な改善と最適化により、データドリブンな意思決定を支援する価値ある監視環境を構築していきましょう。


関連記事: NRQL基礎関連記事: データエクスポート方法