【完全版】Splunk Classic オリジナルチャート作成ガイド
インデックスデータで自由自在にカスタムビジュアライゼーション! 🚀
📚 目次
1️⃣ オリジナルチャートとは?
2️⃣ 基本テンプレート(コピペOK)
3️⃣ チャートパターン集
4️⃣ 実践例:カスタムチャート
5️⃣ カスタマイズ方法
6️⃣ トラブルシューティング
🎯 1️⃣ オリジナルチャートとは?
Splunk標準チャート vs オリジナルチャート
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【比較】
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┌────────────────────────────────────────────────────┐
│ 📊 Splunk標準チャート │
├────────────────────────────────────────────────────┤
│ ✅ 簡単に作れる(クリックだけ) │
│ ❌ デザインの自由度が低い │
│ ❌ 複雑な表現ができない │
│ │
│ 例: Line Chart, Bar Chart, Pie Chart │
└────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ 🎨 オリジナルチャート │
├────────────────────────────────────────────────────┤
│ ✅ デザイン完全自由 │
│ ✅ アニメーション追加可能 │
│ ✅ インタラクティブな表現 │
│ ✅ 独自のUIコンポーネント │
│ ❌ HTML/CSS/JSの知識が必要 │
│ │
│ 例: カスタムゲージ, 3Dグラフ, リアルタイム表示 │
└────────────────────────────────────────────────────┘
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
オリジナルチャートで何ができる?
✅ カスタムメーター(速度計風)
✅ プログレスバー(進捗表示)
✅ カスタムカード(情報表示)
✅ ヒートマップ
✅ ネットワークダイアグラム
✅ 3Dグラフ
✅ リアルタイムアニメーション
✅ インタラクティブダッシュボード
📝 2️⃣ 基本テンプレート
テンプレート1: シンプルカスタムチャート
<dashboard version="1.1">
<label>🎨 オリジナルチャート - 基本</label>
<row>
<panel>
<title>カスタムチャート例</title>
<html>
<style>
.custom-chart {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 15px;
padding: 30px;
color: white;
text-align: center;
margin: 20px;
}
.chart-value {
font-size: 72px;
font-weight: bold;
margin: 20px 0;
}
.chart-label {
font-size: 24px;
opacity: 0.9;
}
</style>
<div class="custom-chart">
<div class="chart-label">エラー件数</div>
<div class="chart-value" id="errorValue">0</div>
<div class="chart-label">件</div>
</div>
<script>
require([
'splunkjs/mvc',
'splunkjs/mvc/searchmanager',
'splunkjs/mvc/simplexml/ready!'
], function(mvc, SearchManager) {
var searchManager = new SearchManager({
id: 'custom_search',
search: 'index=excel_monitoring earliest=-5m (status="NG" OR status="CRITICAL") | stats count',
earliest_time: '-5m',
latest_time: 'now',
autostart: true
});
var results = searchManager.data('results');
results.on('data', function() {
var data = results.data();
if (data && data.results && data.results.length > 0) {
var count = data.results[0].count || 0;
document.getElementById('errorValue').textContent = count;
}
});
});
</script>
</html>
</panel>
</row>
</dashboard>
🎨 3️⃣ チャートパターン集
パターン1: カスタムゲージメーター
<dashboard version="1.1">
<label>⚡ カスタムゲージメーター</label>
<row>
<panel>
<title>CPU使用率ゲージ</title>
<html>
<style>
.gauge-container {
width: 300px;
height: 300px;
margin: 40px auto;
position: relative;
}
.gauge-background {
width: 100%;
height: 100%;
border-radius: 50%;
background: conic-gradient(
#65A637 0deg 108deg,
#F7BC38 108deg 252deg,
#D93F3C 252deg 360deg
);
position: relative;
}
.gauge-inner {
position: absolute;
width: 80%;
height: 80%;
background: white;
border-radius: 50%;
top: 10%;
left: 10%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.gauge-value {
font-size: 64px;
font-weight: bold;
color: #333 ;
}
.gauge-label {
font-size: 18px;
color: #666 ;
margin-top: 10px;
}
.gauge-needle {
position: absolute;
width: 4px;
height: 45%;
background: #333 ;
bottom: 50%;
left: 50%;
transform-origin: bottom center;
transform: translateX(-50%) rotate(0deg);
transition: transform 1s ease;
border-radius: 4px 4px 0 0;
}
.gauge-center {
position: absolute;
width: 20px;
height: 20px;
background: #333 ;
border-radius: 50%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10;
}
</style>
<div class="gauge-container">
<div class="gauge-background">
<div class="gauge-needle" id="gaugeNeedle"></div>
<div class="gauge-center"></div>
<div class="gauge-inner">
<div class="gauge-value" id="gaugeValue">0</div>
<div class="gauge-label">%</div>
</div>
</div>
</div>
<script>
require([
'splunkjs/mvc',
'splunkjs/mvc/searchmanager',
'splunkjs/mvc/simplexml/ready!'
], function(mvc, SearchManager) {
var searchManager = new SearchManager({
id: 'gauge_search',
search: 'index=excel_monitoring earliest=-5m | stats avg(cpu_usage) as avg_cpu',
autostart: true
});
var results = searchManager.data('results');
results.on('data', function() {
var data = results.data();
if (data && data.results && data.results.length > 0) {
var cpu = parseFloat(data.results[0].avg_cpu) || 0;
// 値を表示
document.getElementById('gaugeValue').textContent = Math.round(cpu);
// 針の角度を計算(0-100% を -90deg から 90deg にマッピング)
var angle = (cpu / 100) * 180 - 90;
document.getElementById('gaugeNeedle').style.transform =
'translateX(-50%) rotate(' + angle + 'deg)';
}
});
});
</script>
</html>
</panel>
</row>
</dashboard>
パターン2: プログレスバー
<dashboard version="1.1">
<label>📊 プログレスバー チャート</label>
<row>
<panel>
<title>システム別レスポンスタイム</title>
<html>
<style>
.progress-container {
padding: 30px;
}
.progress-item {
margin: 20px 0;
}
.progress-label {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 16px;
font-weight: bold;
}
.progress-bar-bg {
width: 100%;
height: 40px;
background: #e0e0e0 ;
border-radius: 20px;
overflow: hidden;
position: relative;
}
.progress-bar-fill {
height: 100%;
background: linear-gradient(90deg, #4CAF50 0%, #8BC34A 100%);
border-radius: 20px;
transition: width 1s ease;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 15px;
color: white;
font-weight: bold;
box-shadow: 0 2px 10px rgba(76, 175, 80, 0.4);
}
.progress-bar-fill.warning {
background: linear-gradient(90deg, #FF9800 0%, #FFC107 100%);
box-shadow: 0 2px 10px rgba(255, 152, 0, 0.4);
}
.progress-bar-fill.error {
background: linear-gradient(90deg, #F44336 0%, #E91E63 100%);
box-shadow: 0 2px 10px rgba(244, 67, 54, 0.4);
}
</style>
<div class="progress-container">
<div class="progress-item">
<div class="progress-label">
<span>🌐 WebServer01</span>
<span id="web-value">0 ms</span>
</div>
<div class="progress-bar-bg">
<div class="progress-bar-fill" id="web-bar" style="width: 0%;">
<span id="web-percent">0%</span>
</div>
</div>
</div>
<div class="progress-item">
<div class="progress-label">
<span>🗄️ DBServer01</span>
<span id="db-value">0 ms</span>
</div>
<div class="progress-bar-bg">
<div class="progress-bar-fill" id="db-bar" style="width: 0%;">
<span id="db-percent">0%</span>
</div>
</div>
</div>
<div class="progress-item">
<div class="progress-label">
<span>⚙️ APIGateway01</span>
<span id="api-value">0 ms</span>
</div>
<div class="progress-bar-bg">
<div class="progress-bar-fill" id="api-bar" style="width: 0%;">
<span id="api-percent">0%</span>
</div>
</div>
</div>
</div>
<script>
require([
'splunkjs/mvc',
'splunkjs/mvc/searchmanager',
'splunkjs/mvc/simplexml/ready!'
], function(mvc, SearchManager) {
var searchManager = new SearchManager({
id: 'progress_search',
search: 'index=excel_monitoring earliest=-5m | stats avg(response_time) as avg_rt by system_name',
autostart: true
});
var results = searchManager.data('results');
var maxValue = 1000; // 最大値(ms)
results.on('data', function() {
var data = results.data();
if (data && data.results) {
data.results.forEach(function(row) {
var systemName = row.system_name;
var rt = parseFloat(row.avg_rt) || 0;
var percent = Math.min((rt / maxValue) * 100, 100);
var id = '';
if (systemName === 'WebServer01') id = 'web';
else if (systemName === 'DBServer01') id = 'db';
else if (systemName === 'APIGateway01') id = 'api';
if (id) {
var bar = document.getElementById(id + '-bar');
var valueElem = document.getElementById(id + '-value');
var percentElem = document.getElementById(id + '-percent');
if (bar && valueElem && percentElem) {
bar.style.width = percent + '%';
valueElem.textContent = Math.round(rt) + ' ms';
percentElem.textContent = Math.round(percent) + '%';
// 色を変更
bar.className = 'progress-bar-fill';
if (rt >= 500) bar.classList.add('error');
else if (rt >= 200) bar.classList.add('warning');
}
}
});
}
});
});
</script>
</html>
</panel>
</row>
</dashboard>
パターン3: カスタムカード
<dashboard version="1.1">
<label>🃏 カスタムカード ダッシュボード</label>
<row>
<panel>
<html>
<style>
.cards-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
padding: 20px;
}
.metric-card {
background: white;
border-radius: 20px;
padding: 30px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.metric-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 50px rgba(0,0,0,0.15);
}
.metric-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 5px;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
}
.card-icon {
font-size: 48px;
margin-bottom: 15px;
}
.card-title {
font-size: 18px;
color: #666 ;
margin-bottom: 10px;
font-weight: 500;
}
.card-value {
font-size: 56px;
font-weight: bold;
color: #333 ;
margin: 15px 0;
}
.card-trend {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 5px 12px;
border-radius: 20px;
font-size: 14px;
font-weight: bold;
margin-top: 10px;
}
.card-trend.up {
background: #FFE6E6 ;
color: #D93F3C ;
}
.card-trend.down {
background: #E6F7E6 ;
color: #65A637 ;
}
.card-subtitle {
font-size: 14px;
color: #999 ;
margin-top: 10px;
}
</style>
<div class="cards-grid">
<!-- カード1: エラー件数 -->
<div class="metric-card">
<div class="card-icon">🔴</div>
<div class="card-title">エラー件数</div>
<div class="card-value" id="card1-value">0</div>
<div class="card-trend up" id="card1-trend">
<span>↑</span>
<span id="card1-change">0%</span>
</div>
<div class="card-subtitle">直近5分</div>
</div>
<!-- カード2: 平均レスポンスタイム -->
<div class="metric-card">
<div class="card-icon">⚡</div>
<div class="card-title">平均レスポンスタイム</div>
<div class="card-value" id="card2-value">0<span style="font-size: 24px;">ms</span></div>
<div class="card-trend down" id="card2-trend">
<span>↓</span>
<span id="card2-change">0%</span>
</div>
<div class="card-subtitle">全システム平均</div>
</div>
<!-- カード3: CPU使用率 -->
<div class="metric-card">
<div class="card-icon">💻</div>
<div class="card-title">平均CPU使用率</div>
<div class="card-value" id="card3-value">0<span style="font-size: 24px;">%</span></div>
<div class="card-trend up" id="card3-trend">
<span>↑</span>
<span id="card3-change">0%</span>
</div>
<div class="card-subtitle">全サーバー平均</div>
</div>
<!-- カード4: アクティブシステム -->
<div class="metric-card">
<div class="card-icon">🟢</div>
<div class="card-title">稼働中システム</div>
<div class="card-value" id="card4-value">0<span style="font-size: 24px;">/4</span></div>
<div class="card-trend down" id="card4-trend">
<span>✓</span>
<span id="card4-change">正常</span>
</div>
<div class="card-subtitle">稼働状況</div>
</div>
</div>
<script>
require([
'splunkjs/mvc',
'splunkjs/mvc/searchmanager',
'splunkjs/mvc/simplexml/ready!'
], function(mvc, SearchManager) {
// カード1: エラー件数
var search1 = new SearchManager({
id: 'card1_search',
search: 'index=excel_monitoring earliest=-5m (status="NG" OR status="CRITICAL") | stats count',
autostart: true
});
search1.data('results').on('data', function() {
var data = search1.data('results').data();
if (data && data.results && data.results.length > 0) {
var count = data.results[0].count || 0;
document.getElementById('card1-value').innerHTML = count;
}
});
// カード2: レスポンスタイム
var search2 = new SearchManager({
id: 'card2_search',
search: 'index=excel_monitoring earliest=-5m | stats avg(response_time) as avg_rt',
autostart: true
});
search2.data('results').on('data', function() {
var data = search2.data('results').data();
if (data && data.results && data.results.length > 0) {
var rt = Math.round(data.results[0].avg_rt || 0);
document.getElementById('card2-value').innerHTML = rt + '<span style="font-size: 24px;">ms</span>';
}
});
// カード3: CPU
var search3 = new SearchManager({
id: 'card3_search',
search: 'index=excel_monitoring earliest=-5m | stats avg(cpu_usage) as avg_cpu',
autostart: true
});
search3.data('results').on('data', function() {
var data = search3.data('results').data();
if (data && data.results && data.results.length > 0) {
var cpu = parseFloat(data.results[0].avg_cpu || 0).toFixed(1);
document.getElementById('card3-value').innerHTML = cpu + '<span style="font-size: 24px;">%</span>';
}
});
// カード4: 稼働システム
var search4 = new SearchManager({
id: 'card4_search',
search: 'index=excel_monitoring earliest=-5m | stats dc(system_name) as active_systems',
autostart: true
});
search4.data('results').on('data', function() {
var data = search4.data('results').data();
if (data && data.results && data.results.length > 0) {
var active = data.results[0].active_systems || 0;
document.getElementById('card4-value').innerHTML = active + '<span style="font-size: 24px;">/4</span>';
}
});
});
</script>
</html>
</panel>
</row>
</dashboard>
パターン4: ヒートマップ
<dashboard version="1.1">
<label>🔥 ヒートマップ チャート</label>
<row>
<panel>
<title>時間帯別システム負荷ヒートマップ</title>
<html>
<style>
.heatmap-container {
padding: 30px;
overflow-x: auto;
}
.heatmap-table {
border-collapse: collapse;
margin: 0 auto;
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
}
.heatmap-table th {
background: #f5f5f5 ;
padding: 15px 20px;
text-align: center;
font-weight: bold;
border: 1px solid #ddd ;
}
.heatmap-table td {
padding: 20px 30px;
text-align: center;
border: 1px solid #ddd ;
transition: all 0.3s ease;
cursor: pointer;
font-weight: bold;
font-size: 16px;
}
.heatmap-table td:hover {
transform: scale(1.1);
z-index: 10;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.heat-level-0 { background: #E8F5E9 ; color: #2E7D32 ; }
.heat-level-1 { background: #C8E6C9 ; color: #1B5E20 ; }
.heat-level-2 { background: #FFF9C4 ; color: #F57F17 ; }
.heat-level-3 { background: #FFE082 ; color: #E65100 ; }
.heat-level-4 { background: #FFAB91 ; color: #BF360C ; }
.heat-level-5 { background: #EF5350 ; color: white; }
.heatmap-legend {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 30px;
flex-wrap: wrap;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
}
.legend-color {
width: 30px;
height: 30px;
border-radius: 5px;
border: 1px solid #ddd ;
}
</style>
<div class="heatmap-container">
<table class="heatmap-table">
<thead>
<tr>
<th>System / Hour</th>
<th>10:00</th>
<th>11:00</th>
<th>12:00</th>
<th>13:00</th>
<th>14:00</th>
</tr>
</thead>
<tbody id="heatmapBody">
<tr>
<th>WebServer01</th>
<td class="heat-level-0" id="web-10">0</td>
<td class="heat-level-0" id="web-11">0</td>
<td class="heat-level-0" id="web-12">0</td>
<td class="heat-level-0" id="web-13">0</td>
<td class="heat-level-0" id="web-14">0</td>
</tr>
<tr>
<th>DBServer01</th>
<td class="heat-level-0" id="db-10">0</td>
<td class="heat-level-0" id="db-11">0</td>
<td class="heat-level-0" id="db-12">0</td>
<td class="heat-level-0" id="db-13">0</td>
<td class="heat-level-0" id="db-14">0</td>
</tr>
<tr>
<th>APIGateway01</th>
<td class="heat-level-0" id="api-10">0</td>
<td class="heat-level-0" id="api-11">0</td>
<td class="heat-level-0" id="api-12">0</td>
<td class="heat-level-0" id="api-13">0</td>
<td class="heat-level-0" id="api-14">0</td>
</tr>
</tbody>
</table>
<div class="heatmap-legend">
<div class="legend-item">
<div class="legend-color heat-level-0"></div>
<span>0-20%</span>
</div>
<div class="legend-item">
<div class="legend-color heat-level-1"></div>
<span>20-40%</span>
</div>
<div class="legend-item">
<div class="legend-color heat-level-2"></div>
<span>40-60%</span>
</div>
<div class="legend-item">
<div class="legend-color heat-level-3"></div>
<span>60-80%</span>
</div>
<div class="legend-item">
<div class="legend-color heat-level-4"></div>
<span>80-90%</span>
</div>
<div class="legend-item">
<div class="legend-color heat-level-5"></div>
<span>90-100%</span>
</div>
</div>
</div>
<script>
require([
'splunkjs/mvc',
'splunkjs/mvc/searchmanager',
'splunkjs/mvc/simplexml/ready!'
], function(mvc, SearchManager) {
var searchManager = new SearchManager({
id: 'heatmap_search',
search: 'index=excel_monitoring | eval hour = strftime(_time, "%H") | stats avg(cpu_usage) as avg_cpu by system_name, hour',
autostart: true
});
var results = searchManager.data('results');
function getHeatLevel(value) {
if (value < 20) return 0;
if (value < 40) return 1;
if (value < 60) return 2;
if (value < 80) return 3;
if (value < 90) return 4;
return 5;
}
results.on('data', function() {
var data = results.data();
if (data && data.results) {
data.results.forEach(function(row) {
var system = row.system_name;
var hour = row.hour;
var cpu = parseFloat(row.avg_cpu) || 0;
var id = '';
if (system === 'WebServer01') id = 'web-' + hour;
else if (system === 'DBServer01') id = 'db-' + hour;
else if (system === 'APIGateway01') id = 'api-' + hour;
var cell = document.getElementById(id);
if (cell) {
cell.textContent = Math.round(cpu) + '%';
cell.className = 'heat-level-' + getHeatLevel(cpu);
}
});
}
});
});
</script>
</html>
</panel>
</row>
</dashboard>
🎯 4️⃣ 実践例:総合ダッシュボード
全部入り!カスタムダッシュボード
<dashboard version="1.1">
<label>🎨 オリジナルチャート総合ダッシュボード</label>
<description>インデックスデータで作るカスタムビジュアライゼーション</description>
<!-- Row 1: カスタムカード -->
<row>
<panel>
<html>
<style>
.mini-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 15px;
padding: 15px;
}
.mini-card {
background: white;
border-radius: 12px;
padding: 20px;
text-align: center;
box-shadow: 0 5px 20px rgba(0,0,0,0.08);
border-left: 4px solid;
}
.mini-card.success { border-left-color: #4CAF50 ; }
.mini-card.warning { border-left-color: #FF9800 ; }
.mini-card.error { border-left-color: #F44336 ; }
.mini-card.info { border-left-color: #2196F3 ; }
.mini-icon {
font-size: 32px;
margin-bottom: 10px;
}
.mini-value {
font-size: 36px;
font-weight: bold;
margin: 10px 0;
}
.mini-label {
font-size: 14px;
color: #666 ;
}
</style>
<div class="mini-cards">
<div class="mini-card success">
<div class="mini-icon">✅</div>
<div class="mini-value" id="ok-count">0</div>
<div class="mini-label">正常</div>
</div>
<div class="mini-card warning">
<div class="mini-icon">⚠️</div>
<div class="mini-value" id="warning-count">0</div>
<div class="mini-label">警告</div>
</div>
<div class="mini-card error">
<div class="mini-icon">🔴</div>
<div class="mini-value" id="error-count">0</div>
<div class="mini-label">エラー</div>
</div>
<div class="mini-card info">
<div class="mini-icon">📊</div>
<div class="mini-value" id="total-count">0</div>
<div class="mini-label">総イベント</div>
</div>
</div>
<script>
require([
'splunkjs/mvc',
'splunkjs/mvc/searchmanager',
'splunkjs/mvc/simplexml/ready!'
], function(mvc, SearchManager) {
var search = new SearchManager({
id: 'status_search',
search: 'index=excel_monitoring earliest=-5m | stats count by status',
autostart: true
});
search.data('results').on('data', function() {
var data = search.data('results').data();
if (data && data.results) {
var ok = 0, warning = 0, error = 0, total = 0;
data.results.forEach(function(row) {
var count = parseInt(row.count) || 0;
total += count;
if (row.status === 'OK') ok = count;
else if (row.status === 'WARNING') warning = count;
else if (row.status === 'NG' || row.status === 'CRITICAL') error += count;
});
document.getElementById('ok-count').textContent = ok;
document.getElementById('warning-count').textContent = warning;
document.getElementById('error-count').textContent = error;
document.getElementById('total-count').textContent = total;
}
});
});
</script>
</html>
</panel>
</row>
<!-- Row 2: ゲージとプログレスバー -->
<row>
<panel>
<title>⚡ CPU使用率ゲージ</title>
<html>
<style>
.small-gauge {
width: 200px;
height: 200px;
margin: 20px auto;
position: relative;
}
.small-gauge-bg {
width: 100%;
height: 100%;
border-radius: 50%;
background: conic-gradient(
#65A637 0deg 108deg,
#F7BC38 108deg 252deg,
#D93F3C 252deg 360deg
);
}
.small-gauge-inner {
position: absolute;
width: 75%;
height: 75%;
background: white;
border-radius: 50%;
top: 12.5%;
left: 12.5%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.small-value {
font-size: 48px;
font-weight: bold;
}
</style>
<div class="small-gauge">
<div class="small-gauge-bg">
<div class="small-gauge-inner">
<div class="small-value" id="cpu-gauge">0</div>
<div style="font-size: 14px; color: #666 ;">%</div>
</div>
</div>
</div>
<script>
require([
'splunkjs/mvc',
'splunkjs/mvc/searchmanager',
'splunkjs/mvc/simplexml/ready!'
], function(mvc, SearchManager) {
var search = new SearchManager({
id: 'cpu_gauge_search',
search: 'index=excel_monitoring earliest=-5m | stats avg(cpu_usage) as cpu',
autostart: true
});
search.data('results').on('data', function() {
var data = search.data('results').data();
if (data && data.results && data.results.length > 0) {
var cpu = Math.round(data.results[0].cpu || 0);
document.getElementById('cpu-gauge').textContent = cpu;
}
});
});
</script>
</html>
</panel>
<panel>
<title>📊 システム別負荷</title>
<html>
<style>
.compact-progress {
padding: 20px;
}
.compact-item {
margin: 15px 0;
}
.compact-label {
font-size: 14px;
margin-bottom: 5px;
display: flex;
justify-content: space-between;
}
.compact-bar-bg {
width: 100%;
height: 25px;
background: #e0e0e0 ;
border-radius: 12px;
overflow: hidden;
}
.compact-bar {
height: 100%;
background: linear-gradient(90deg, #2196F3 , #21CBF3 );
border-radius: 12px;
transition: width 0.8s ease;
}
</style>
<div class="compact-progress">
<div class="compact-item">
<div class="compact-label">
<span>WebServer01</span>
<span id="web-cpu">0%</span>
</div>
<div class="compact-bar-bg">
<div class="compact-bar" id="web-bar" style="width: 0%"></div>
</div>
</div>
<div class="compact-item">
<div class="compact-label">
<span>DBServer01</span>
<span id="db-cpu">0%</span>
</div>
<div class="compact-bar-bg">
<div class="compact-bar" id="db-bar" style="width: 0%"></div>
</div>
</div>
<div class="compact-item">
<div class="compact-label">
<span>APIGateway01</span>
<span id="api-cpu">0%</span>
</div>
<div class="compact-bar-bg">
<div class="compact-bar" id="api-bar" style="width: 0%"></div>
</div>
</div>
</div>
<script>
require([
'splunkjs/mvc',
'splunkjs/mvc/searchmanager',
'splunkjs/mvc/simplexml/ready!'
], function(mvc, SearchManager) {
var search = new SearchManager({
id: 'system_cpu_search',
search: 'index=excel_monitoring earliest=-5m | stats avg(cpu_usage) as cpu by system_name',
autostart: true
});
search.data('results').on('data', function() {
var data = search.data('results').data();
if (data && data.results) {
data.results.forEach(function(row) {
var cpu = parseFloat(row.cpu) || 0;
var id = '';
if (row.system_name === 'WebServer01') id = 'web';
else if (row.system_name === 'DBServer01') id = 'db';
else if (row.system_name === 'APIGateway01') id = 'api';
if (id) {
document.getElementById(id + '-cpu').textContent = cpu.toFixed(1) + '%';
document.getElementById(id + '-bar').style.width = cpu + '%';
}
});
}
});
});
</script>
</html>
</panel>
</row>
<!-- Row 3: Splunk標準チャート -->
<row>
<panel>
<title>📈 時系列グラフ(標準チャート)</title>
<chart>
<search>
<query>
index=excel_monitoring earliest=-1h
| timechart span=5m avg(response_time) as "Avg RT" by system_name
</query>
</search>
<option name="charting.chart">line</option>
<option name="charting.legend.placement">bottom</option>
</chart>
</panel>
</row>
</dashboard>
🛠️ 5️⃣ カスタマイズ方法
カスタマイズポイント
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【カスタマイズできる要素】
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. 色
├─ background: #色コード
├─ color: #色コード
└─ border-color: #色コード
2. サイズ
├─ width: 300px
├─ height: 200px
├─ font-size: 24px
└─ padding: 20px
3. アニメーション
├─ transition: all 0.3s ease
├─ @keyframes で独自アニメーション
└─ transform: scale(1.1)
4. レイアウト
├─ display: grid
├─ flex
└─ position: absolute
5. データ取得元
└─ search: 'index=〇〇 | ...'
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
色変更例
/* 緑系 → 青系に変更 */
/* 変更前 */
background: linear-gradient(90deg, #4CAF50 0%, #8BC34A 100%);
/* 変更後 */
background: linear-gradient(90deg, #2196F3 0%, #03A9F4 100%);
サイズ変更例
/* 小さくする */
.gauge-container {
width: 200px; /* 300px → 200px */
height: 200px; /* 300px → 200px */
}
.gauge-value {
font-size: 48px; /* 64px → 48px */
}
データ取得クエリ変更例
// 変更前: CPU使用率
search: 'index=excel_monitoring | stats avg(cpu_usage) as cpu'
// 変更後: メモリ使用率
search: 'index=excel_monitoring | stats avg(memory_usage) as memory'
🔧 6️⃣ トラブルシューティング
問題1: チャートが表示されない
【原因】
├─ HTMLの構文エラー
├─ JavaScriptのエラー
└─ CSSの記述ミス
【確認方法】
1. ブラウザでF12キーを押す
2. Consoleタブを確認
3. 赤いエラーメッセージを確認
【対処法】
1. XMLの<html>タグが正しく閉じているか
2. <script>タグが正しく閉じているか
3. シングルクォート/ダブルクォートの対応
問題2: データが更新されない
【原因】
├─ search id が間違っている
├─ インデックス名が間違っている
└─ フィールド名が間違っている
【確認】
1. Search画面でクエリを直接実行
2. 結果が返ってくるか確認
3. フィールド名が一致しているか確認
問題3: スタイルが適用されない
【原因】
CSSセレクタが間違っている
【確認】
<div class="my-class"> ← クラス名
↓
.my-class { ... } ← CSS(ドット付き)
<div id="my-id"> ← ID名
↓ #my -id { ... } ← CSS(シャープ付き)
📚 まとめ
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【オリジナルチャート作成の流れ】
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. ダッシュボード作成
↓
2. <html>パネル追加
↓
3. <style> で見た目を作る
↓
4. HTML要素を配置
↓
5. <script> でデータ取得
↓
6. 値を表示
↓
7. 完成!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【重要ポイント】
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ HTML/CSS/JSの基礎知識があればOK
✅ テンプレートをコピペして改造
✅ 色・サイズは自由に変更可能
✅ インデックスデータから自動取得
✅ リアルタイム更新可能
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
これでオリジナルチャートが作れますね! 🎉
テンプレートをコピペして、色やサイズを変更するだけで 自分だけのカスタムダッシュボードが完成します! 💪
どのチャートを作りたいか教えてください! 一緒にカスタマイズしましょう! 🚀