JavaScript
d3.js
Jenkins
c3.js

C3.jsでJenkinsのグラフを格好よくする

More than 1 year has passed since last update.

C3.jsとは

C3.js(リンク)は、簡単にチャート作成することができるJavaScriptのライブラリです。内部でD3.jsを利用しています。

C3.jsは、以下のようなチャートを簡単に作成することができます。

Jenkinsのビルド単位のデータをC3.jsでグラフにする

Jenkinsのビルド毎の各種結果データを、独自に解析してC3.jsでグラフにするケースを考えてみます。

JenkinsのPlot Plugin(リンク)はお手軽ではありますが、見た目がそんなにかっこよくないのが悲しいですね。
JenkinsのPlot Pluginは、こんな感じのグラフです。


examplePlot.png
Jenkins: Plot Plugin

一方、C3.jsでグラフを作ると、こんな感じになります。
マウスカーソルを重ねると値がポップアップ表示されます。


area_chart.png
C3.js: エリア チャート



stacked_area_chart.png
C3.js: 積層エリア チャート

C3.jsのプログラム

今回は、上記のエリアチャート、積層エリアチャートをC3.jsで描いてみます。

チャート概要

 X軸:飛び飛びのビルド番号 #95, #96, #99, #100, #101, #105
 Y軸:プログラム言語毎のコード行数の差分(lines delta)
です。

ディレクトリ構成

HTML、JavaScript、CSSで構成されています。
ディレクトリ構成は、以下のようになります。
report/
 ├ css/
 │ └ lib
 │   └ c3.min.css
 ├ js/
 │ ├ lib
 │ │ ├ c3.min.js
 │ │ └ d3.v3.min.js
 │ └ data.js
 └ index.html

このうち、自分で書いたのは、data.jsとindex.htmlです。

インストール

c3.min.cssとc3.min.jsは、こちらから(リンク)ダウンロードしてください。
d3.v3.min.jsは、こちらから(リンク)ダウンロードしたd3.min.jsをd3.min.jsをd3.v3.min.jsにリネームしてください。C3.jsのチュートリアルに、D3.jsのv3.x系を使うように書いてありましたので、今回、D3.jsはv3.x系のv3.5.17を利用しています。

プログラム

index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>C3.js chart</title>
    <link href="./css/lib/c3.min.css" rel="stylesheet" type="text/css">
  </head>
  <body>
    <div id="chart"></div>
    <script src="./js/lib/d3.v3.min.js" charset="utf-8"></script>
    <script src="./js/lib/c3.min.js"></script>
    <script src="./js/data.js"></script>
  </body>
</html>

エリアチャート

data.js
var chart = c3.generate({
    bindto: '#chart',
    data: {
        x: 'x',
        columns: [
            ['x', 1, 2, 3, 4, 5, 6],
            ['HTML', 40, 50, -20, 40, 0, -50],
            ['CSS', 30, 20, -120, 200, 150, 250],
            ['JavaScript', 130, 100, 140, -90, -150, 50]
        ],
        type: 'area',
        //labels: true
    },
    axis: {
        x: {
            show: false,
            padding: {
                left: 0.15,
                right:0.1
            }
        },
        y: {
            label: {
                text: 'lines delta',
                position: 'outer-middle',
            }
        }
    },
    grid: {
        x: {
            lines: [
                {value: 1, text: '#95', position: 'start'},
                {value: 2, text: '#96', position: 'start'},
                {value: 3, text: '#99', position: 'start'},
                {value: 4, text: '#100', position: 'start'},
                {value: 5, text: '#101', position: 'start'},
                {value: 6, text: '#105', position: 'start'},
            ]
        },
        y: {
            lines: [
                {value: 0}
            ],
            show: true,
        }
    }
});

積層エリアチャート

data.js
var chart = c3.generate({
    bindto: '#chart',
    data: {
        x: 'x',
        columns: [
            ['x', 1, 2, 3, 4, 5, 6],
            ['HTML', 40, 50, 20, 40, 0, 50],
            ['CSS', 30, 20, 120, 200, 150, 250],
            ['JavaScript', 130, 100, 140, 90, 150, 50]
        ],
        type: 'area',
        groups: [
            ['HTML', 'CSS', 'JavaScript']
        ]
    },
    axis: {
        x: {
            show: false,
            padding: {
                left: 0.15,
                right:0.1
            }
        },
        y: {
            label: {
                text: 'lines delta',
                position: 'outer-middle',
            }
        }
    },
    grid: {
        x: {
            lines: [
                {value: 1, text: '#95', position: 'start'},
                {value: 2, text: '#96', position: 'start'},
                {value: 3, text: '#99', position: 'start'},
                {value: 4, text: '#100', position: 'start'},
                {value: 5, text: '#101', position: 'start'},
                {value: 6, text: '#105', position: 'start'},
            ]
        },
        y: {
            lines: [
                {value: 0}
            ],
            show: true,
        }
    }
});

Jenkins側の配置

最終的に、HTML, JavaScript, CSSをJenkinsのuserContent(ex. /var/lib/jenkins/userContent)に置いて、HTML Publisher Plugin(リンク)でリンクを貼って表示するとよいです。

今回の例では、データがJavaScript内に埋め込まれていましたが、csvやjson形式で定義することも可能です。外部データとして定義したい場合は、こちらを参考にしてください。(データをscvやjsonで定義する方法
Jenkinsのビルドの一環でshellでデータを作るとよいかと思います。

C3.jsメモ

C3.jsでチャートを描くときに、Jenkinsのビルド番号が飛び飛びになることへの対応が少し工夫が必要でした。

Jenkinsのビルド番号

Jenkinsでは、特定のビルドを手動で削除したり、結果データがビルドが成功したときしか取得できないことがあります。よって、表示データのビルド番号が飛び飛びになってしまいます。グラフにする際は、飛び飛びになっている各ビルドに対して、1ビルドあたりの幅を同じにしたいところです。

type: 'category'の限界

x軸のデータをビルド番号通りの飛び飛びの数字にしてしまうと、自動的に数値通りの幅になってしまい、1ビルドあたりの幅が同じになりません。そこで、ビルド番号が飛び飛びになっていても、同じ幅になるように、type: 'category'を指定します。これにより飛び飛びのビルド番号であっても、同じ幅のチャートになります。この方法、一見、簡単なのですが、どうしても、グラフの両端にスペースが空いてしまい見栄えがよくありません。C3.jsのリファレンスを見てもaxis.x.paddingの項には、「On category axis, this option will be ignored.(categoryを指定した場合は、paddingの指定は効きません。)」と書かれています。また、C3.jsのフォーラムには、質問(X axis category padding)は上がっているものの、回答はなし。type: 'category'は、棒グラフや折れ線グラフ向きの設定で、エリアチャートを格好よく描くのには向いていないようです。

x軸のデータを文字列で指定する

axis.x.typeをindexedに指定した状態でx軸のデータを文字列(例えば「#19」)で指定すると、データはy軸上にプロットされ、チャートになりませんでした。

最終的に

最終的には、上記のようにx軸に1~の通し番号を振っておき、通し番号にグリッドを設定し、そのグリッドにビルド番号のラベルを付けて対応することになりました。