Python 的 Matplotlib 函式庫提供強大的圖表繪製功能,但預設顯示在互動式視窗中,不利於自動化報告。本文介紹使用 Agg 後端直接將圖表儲存成檔案,方便整合到自動化系統。程式碼範例示範瞭如何使用 matplotlib.use('Agg') 設定後端,並使用 plt.savefig() 儲存圖表為 PNG 和 PDF 格式。結合資料函式庫操作,可以定期從資料函式庫讀取資料,自動生成統計圖表。文章也說明瞭如何使用 SQL 查詢從資料函式庫檢索主機和探針資料,並計算服務可用性等統計指標。最後,利用 Jinja2 範本引擎,將資料和圖表整合到網頁報告中,實作自動化報告生成。網站結構設計包含首頁、主機列表、主機詳情、探針詳情和時間尺度圖表等頁面,方便使用者瀏覽和分析資料。程式碼範例展示瞭如何使用 Jinja2 範本生成網頁,並動態嵌入圖表和統計資料。
統計資料收集與報告生成
將圖表儲存至檔案
到目前為止,我們已經探討了圖表生成的各種導向。你已經瞭解生成的圖表會在GUI中的互動式視窗中顯示。如果你只需要快速檢查結果,這是完全可以接受的,但這也意味著每次你想檢檢視表時,都需要執行完整的計算過程。你可以選擇從圖表顯示視窗中儲存圖表,但這是一個手動過程,不適合自動化報告系統。
matplotlib 使用影像後端處理程式來生成影像。對於大多數人來說,只需使用最流行的格式,如PNG、PDF、SVG、PS和EPS,matplotlib 提供了 Anti-Grain Geometry (Agg) 後端,它在後台使用C++ anti-grain影像渲染引擎。預設情況下,matplotlib 在匯入pyplot模組時使用其中一個GUI引擎(例如wxPython)。要改變這種行為,你必須首先指示它使用Agg後端,然後匯入pyplot。
使用 Agg 後端儲存影像至檔案
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
x = np.arange(100)
y = np.sin(2 * np.pi * x / 100)
ax.plot(y)
plt.savefig('sin-wave.png')
plt.savefig('sin-wave.pdf')
內容解密:
- 匯入必要的模組:首先匯入
matplotlib、matplotlib.pyplot和numpy。這裡特別需要注意的是,matplotlib.use('Agg')的呼叫必須在匯入matplotlib.pyplot之前,以確保使用 Agg 後端。 - 建立圖形和軸:使用
plt.figure()建立一個新的圖形,並使用fig.add_subplot(1, 1, 1)在這個圖形中新增一個軸。 - 生成資料:使用
np.arange(100)生成一個包含0到99的陣列x,並計算y = np.sin(2 * np.pi * x / 100)得到對應的正弦值。 - 繪製圖形:使用
ax.plot(y)將y對應的值繪製在軸上。 - 儲存圖形:最後,使用
plt.savefig()將生成的圖形儲存為 PNG 和 PDF 格式的檔案。
圖表統計資料
我們花了很多時間討論各種統計資料分析方法。你知道如何檢查資料集中是否有趨勢,以及趨勢是正向還是負向。你還知道如何計算資料集的平均值,以及資料落在預定義邊界(標準差)內的可能性。現在讓我們看看如何應用這些知識。我們將構建一個簡單的應用程式,定期執行並生成狀態頁面。這些頁面是靜態頁面,將由 Apache 網頁伺服器提供服務。
從資料函式庫收集資料
第9章提供了我們用於監控系統的各種資料函式庫表格的詳細資訊,以及它們之間的關聯。因為我們在本章的例子中感興趣的是報告探針讀數,所以最感興趣的是 probereading 表格,它包含了從感測器獲得的原始資料。在處理之前,需要對這個表格的值進行過濾,所以我們需要知道這個讀數屬於哪個感測器——或者更準確地說,哪個探針。我們還需要按讀取這些探針的主機對探針讀數進行分組。換句話說,我們需要遍歷 host 表格中的所有條目;然後對於找到的每個主機,我們需要檢查哪些探針正在其上執行。一旦我們確定了整個主機到探針的組合,我們就需要取得隨時間變化的感測器讀數。
從資料函式庫檢索所有主機及相關探針
class SiteGenerator:
def __init__(self, db_name):
self.db_name = db_name
self.conn = sqlite3.connect(self.db_name)
self.hosts = []
self._get_all_hosts()
def _get_all_hosts(self):
for h in self.conn.execute("SELECT * FROM host"):
host_entry = list(h)
query_str = """ SELECT hostprobe.id,
probe.name,
COALESCE(hostprobe.warning, probe.warning),
COALESCE(hostprobe.error, probe.error)
FROM probe,
hostprobe
WHERE probe.id = hostprobe.probe_id AND
hostprobe.host_id = ?
"""
probes = self.conn.execute(query_str, (h[0],)).fetchall()
host_entry.append(probes)
self.hosts.append(host_entry)
內容解密:
SiteGenerator類別初始化:在初始化時,連線到指定的 SQLite 資料函式庫,並初始化一個空列表hosts用於儲存主機資訊。_get_all_hosts方法:遍歷host表格中的所有主機,對於每個主機,查詢相關的探針資訊,包括探針ID、名稱、警告和錯誤閾值。- 使用
COALESCE函式:該函式傳回列表中的第一個非空值,用於處理主機特定閾值設定。如果主機特定閾值未設定(即為 NULL),則回退到預設值。
繪製時間尺度圖表
現在,我們已經擁有了進一步資料處理所需的所有資訊:主機和相關的主機探針。有很多不同的方法來表示我們收集的統計資訊。在本例中,我們將按照兩個引數之一對資訊進行排序:探針名稱和時間尺度。為了簡化實作,我們將使用預定義的可用時間尺度列表:1天、7天和30天。
網站結構規劃
為了開發範本和對應的程式碼,我發現先視覺化網站結構非常有幫助。圖11-7表示了我們的網站結構,以及示例HTML檔案名稱(ID待替換為實際值)和對應的Jinja2範本。
網站結構圖示
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title Python 自動化圖表生成與統計報告
package "統計分析流程" {
package "資料收集" {
component [樣本資料] as sample
component [母體資料] as population
}
package "描述統計" {
component [平均數/中位數] as central
component [標準差/變異數] as dispersion
component [分佈形狀] as shape
}
package "推論統計" {
component [假設檢定] as hypothesis
component [信賴區間] as confidence
component [迴歸分析] as regression
}
}
sample --> central : 計算
sample --> dispersion : 計算
central --> hypothesis : 檢驗
dispersion --> confidence : 估計
hypothesis --> regression : 建模
note right of hypothesis
H0: 虛無假設
H1: 對立假設
α: 顯著水準
end note
@enduml
此圖示展示了網站的基本導航結構,從首頁到主機列表,再到各個主機和探針的詳情頁面,最終到達顯示不同時間尺度圖表的頁面。
圖示內容解密:
- 首頁:網站的首頁,通常包含導航連結或主要資訊的概覽。
- 主機列表:列出所有被監控的主機,供使用者選擇檢視詳情。
- 主機詳情:顯示特定主機上的探針及其狀態資訊。
- 探針詳情:提供特定探針的詳細資訊,包括其讀數和狀態。
- 時間尺度圖表:根據選定的時間尺度(例如1天、7天、30天),展示探針讀數的變化趨勢。
透過這種結構,使用者可以方便地導航至感興趣的探針詳情頁面,並檢視其在不同時間尺度下的表現。
網站統計資訊產生與網頁呈現
在建立網站統計資訊的過程中,我們首先需要產生主機列表頁面。這個頁面相對簡單,主要任務是將主機列表傳遞給範本引擎進行渲染。
主機列表頁面產生
主機列表頁面的產生主要依賴於 _generate_hosts_view 方法:
def _generate_hosts_view(self):
t = self.tpl_env.get_template('index.template')
f = open("%s/index.html" % self.location, 'w')
f.write(t.render({'hosts': self.hosts}))
f.close()
內容解密:
self.tpl_env.get_template('index.template'):載入名為index.template的範本檔案,用於渲染主機列表頁面。open("%s/index.html" % self.location, 'w'):開啟(或建立)一個名為index.html的檔案於指定位置,準備寫入渲染後的內容。t.render({'hosts': self.hosts}):將self.hosts中的主機列表傳遞給範本引擎進行渲染,生成最終的 HTML 內容。f.write(...)和f.close():將渲染後的內容寫入index.html檔案,並關閉檔案。
主機列表範本
範本檔案 index.template 的內容如下:
<h1>Hosts</h1>
<ul>
{% for host in hosts %}
<li><a href="host_{{ host[0] }}_details.html">{{ host[1] }}</a>
({{ host[2] }}:{{ host[3] }})</li>
{% endfor %}
</ul>
內容解密:
{% for host in hosts %}:迴圈遍歷hosts列表中的每個主機。<a href="host_{{ host[0] }}_details.html">{{ host[1] }}</a>:為每個主機生成一個超連結,連結到對應的主機詳細資訊頁面,顯示主機名稱。({{ host[2] }}:{{ host[3] }}):顯示主機的位址和監控代理的連線埠號。
主機詳細資訊頁面
主機詳細資訊頁面的產生涉及到兩個主要方法: _generate_host_toc 和 _calculate_service_availability。
_generate_host_toc 方法
def _generate_host_toc(self, host):
probe_sa = {}
for probe in host[4]:
probe_sa[probe[1]] = {}
for scale in TIMESCALES:
probe_sa[probe[1]][scale] = self._calculate_service_availability(probe, scale)
t = self.tpl_env.get_template('host.template')
f = open("%s/host_%s_details.html" % (self.location, host[0]), 'w')
f.write(t.render({ 'host': host, 'timescales': TIMESCALES, 'probe_sa': probe_sa }))
f.close()
內容解密:
probe_sa = {}:初始化一個字典,用於儲存每個探針在不同時間尺度下的服務可用性資料。- 雙層迴圈:遍歷每個主機的探針和不同的時間尺度,呼叫
_calculate_service_availability計算服務可用性。 t.render({...}):將主機資訊、時間尺度和探針服務可用性資料傳遞給範本引擎進行渲染。
_calculate_service_availability 方法
def _calculate_service_availability(self, probe, scale):
# ...
sampling_rate = self.conn.execute("""SELECT probeinterval FROM probingschedule WHERE hostprobe_id=?""", (probe[0],)).fetchone()[0]
records_to_read = int(24 * 60 * scale / sampling_rate)
# ...
內容解密:
sampling_rate:從資料函式庫中查詢指定探針的取樣率。records_to_read:根據取樣率和指定的時間尺度計算需要讀取的記錄數量。- SQL 查詢:執行巢狀 SQL 查詢,計算超過閾值的記錄數量,從而得出服務可用性指標。
網站結構與資訊呈現
網站結構如圖 11-7 所示,主要包含以下幾個部分:
- 首頁:列出所有可用的主機連結。
- 主機詳細資訊頁面:顯示每個主機的服務可用性統計資料,並提供不同時間尺度和探針的圖表連結。
- 時間尺度詳細資訊頁面:提供特定主機在不同時間尺度下的圖表連結。
- 探針詳細資訊頁面:提供特定探針在不同時間尺度下的圖表連結。
透過上述結構和實作,能夠有效地呈現監控資料,並提供豐富的統計資訊和分析結果。
統計資料收集與報告生成
本章節將詳細介紹如何收集統計資料並生成報告。我們將探討相關的程式碼實作,包括主機詳情範本、圖表集合頁面以及效能圖表的繪製。
主機詳情範本
主機詳情範本負責顯示主機的可用性統計資料,並生成指向包含圖表的頁面的連結。以下為範本的程式碼範例:
<h1>主機詳情:{{ host[1] }}</h1>
<h2>按時間尺度分組的檢視</h2>
<p>您可以在此找到此主機上所有可用的探針在相同時間尺度上的檢視。</p>
<ul>
{% for scale in timescales %}
<li><a href="hsd_{{ host[0] }}_{{ scale }}.html">{{ scale }} 天檢視</a></li>
{% endfor %}
</ul>
<h2>按探針分組的檢視</h2>
<p>您可以在此找到相同探針的所有可用時間尺度檢視。</p>
<ul>
{% for probe in host[4] %}
<li><a href="hpd_{{ probe[0] }}.html">{{ probe[1] }}</a></li>
{% endfor %}
</ul>
<h2>主機統計資料</h2>
<h3>服務可用性詳情</h3>
{% for probe in probe_sa %}
<h4>"{{ probe }}" 檢查的可用性</h4>
<ul>
{% for scale in probe_sa[probe] %}
<li>在 {{ scale }} 天尺度上:
<ul>
<li>警告:{{ probe_sa[probe][scale][0]|round(3) }}%</li>
<li>錯誤:{{ probe_sa[probe][scale][1]|round(3) }}%</li>
</ul>
</li>
{% endfor %}
</ul>
{% endfor %}
內容解密:
- 此範本使用 Jinja2 語法動態生成內容。
host[1]和host[0]分別代表主機的名稱和 ID。timescales是一個列表,包含不同的時間尺度值,用於生成對應的連結。host[4]是一個列表,包含主機上的探針資訊。probe_sa是一個字典,包含每個探針在不同時間尺度上的可用性統計資料。
圖表集合頁面
圖表集合頁面負責展示不同探針或時間尺度下的圖表。我們定義了兩個函式:_generate_host_probe_details 和 _generate_host_scale_details,分別用於生成特定探針和特定時間尺度下的圖表集合頁面。
def _generate_host_probe_details(self, host_struct, probe_struct):
t = self.tpl_env.get_template('host_probe_details.template')
f = open("%s/hpd_%s.html" % (self.location, probe_struct[0]), 'w')
images = []
for scale in TIMESCALES:
images.append([scale, "plot_%s_%s.png" % (probe_struct[0], scale)])
f.write(t.render({'host': host_struct, 'probe': probe_struct, 'images': images}))
f.close()
def _generate_host_scale_details(self, host_struct, scale):
t = self.tpl_env.get_template('host_scale_details.template')
f = open("%s/hsd_%s_%s.html" % (self.location, host_struct[0], scale), 'w')
images = []
for probe in host_struct[4]:
images.append([probe[1], "plot_%s_%s.png" % (probe[0], scale)])
f.write(t.render({'host': host_struct, 'scale': scale, 'images': images}))
f.close()
內容解密:
_generate_host_probe_details函式生成特定探針在不同時間尺度下的圖表集合頁面。_generate_host_scale_details函式生成特定時間尺度下不同探針的圖表集合頁面。- 兩個函式都使用範本引擎渲染範本,並將生成的 HTML 寫入檔案。
images列表包含圖表的相關資訊,包括圖表的標題和檔案名稱。
效能圖表繪製
效能圖表繪製函式 _plot_time_graph 負責從資料函式庫中讀取資料並生成效能圖表。
def _plot_time_graph(self, hostprobe_id, time_window, sampling_rate, plot_title, plot_file_name, warn=None, err=None):
records_to_read = int(time_window / sampling_rate)
records = self.conn.execute("""SELECT timestamp, probe_value FROM probereading WHERE hostprobe_id=? LIMIT ?""", (hostprobe_id, records_to_read)).fetchall()
time_array, val_array = zip(*records)
mean = np.mean(val_array)
std = np.std(val_array)
warning_val = mean + 3 * std
error_val = mean + 4 * std
data_y = np.array(val_array)
data_x = np.arange(len(data_y))
data_time = [dateutil.parser.parse(s) for s in time_array]
data_xtime = matplotlib.dates.date2num(data_time)
a, b = np.polyfit(data_x, data_y, 1)
matplotlib.rcParams['font.size'] = 10
fig = plt.figure(figsize=(8,4))
ax = fig.add_subplot(1, 1, 1)
ax.set_title(plot_title + "\nMean: %.2f, Std Dev: %.2f, Warn Lvl: %.2f, Err Lvl: %.2f" % (mean, std, warning_val, error_val))
ax.plot_date(data_xtime, data_y, 'b')
ax.plot_date(data_xtime, data_x * a + b, color='black', linewidth=3, marker='None', linestyle='-', alpha=0.5)
內容解密:
- 此函式從資料函式庫中讀取特定主機探針的資料,並計算相關的統計引數,如平均值和標準差。
- 使用 NumPy 和 matplotlib 繪製效能圖表,並在圖表中顯示趨勢線。
- 圖表的標題包含相關的統計資訊,如平均值、標準差、警告和錯誤閾值。