Skip to main content

Guitar Pro file parsing, rendering, and audio playback engine library

Project description

ApolloTab

Python 3.8+ License: MPL 2.0 PyPI version

Guitar Pro File Parsing, Rendering, and Audio Playback Engine Library

ApolloTab is a fully-featured Python library for parsing, rendering, and playing Guitar Pro (.gp3/.gp4/.gp5/.gpx) tablature files.

Features

  • File Parsing: Full support for GP3/GP4/GP5/GPX formats — extract song info, tracks, measures, notes, technique markings
  • Tablature Rendering: Render high-quality tablature images (QPixmap) using QPainter with multi-page output
  • Audio Playback: Real-time MIDI synthesis engine based on FluidSynth with SoundFont support
  • Technique Support: 18 playing techniques (hammer-on, pull-off, bend, slide, harmonic, vibrato, etc.)
  • Highly Configurable: Fully adjustable rendering parameters (line width, spacing, colors, fonts, etc.)

Installation

Install from PyPI (Recommended)

pip install ApolloTab

Install from Source

git clone https://github.com/your-repo/ApolloTab.git
cd ApolloTab

pip install -e .

# Or install dev environment (with test tools)
pip install -e ".[dev]"

Quick Start

1. Parse a GTP File

from ApolloTab import parse_gtp

song = parse_gtp("my_song.gp5")

print(f"Title: {song.title}")
print(f"Artist: {song.artist}")
print(f"BPM: {song.tempo}")
print(f"Track count: {song.track_count}")

for track in song.tracks:
    print(f"\nTrack: {track.name}")
    print(f"Tuning: {track.strings}")
    print(f"Measures: {len(track.measures)}")

2. Render Tablature Images

from ApolloTab import render_gtp

pages = render_gtp("my_song.gp5", track_index=0)

for i, page in enumerate(pages):
    page.save(f"output_page_{i + 1}.png")
    print(f"Saved page {i + 1}")

# Or use TabRenderer for finer control
from ApolloTab import TabRenderer, RenderConfig

config = RenderConfig(
    page_width=2480,
    page_height=3508,
    line_color="#000000",
)

renderer = TabRenderer(config=config)
pages = renderer.render(song, track_index=0)

layouts = renderer.last_layouts  # List[PageLayout]

3. Audio Playback

from ApolloTab import (
    parse_gtp,
    MidiConverter,
    SynthEngine,
)

song = parse_gtp("my_song.gp5")

converter = MidiConverter()
events = converter.convert(song, track_index=0)

engine = SynthEngine()
engine.initialize()
engine.load_soundfont()
engine.set_instrument(0, 27)  # Electric Guitar (MIDI program 27)

engine.load_events(events, bpm=song.tempo)
engine.play()

import time
time.sleep(5)
engine.pause()
time.sleep(2)
engine.resume()
time.sleep(5)

engine.stop()

4. Complete Example: Parse -> Render -> Play

from ApolloTab import (
    parse_gtp,
    TabRenderer,
    MidiConverter,
    SynthEngine,
    RenderConfig,
)

def process_gtp_file(file_path: str, track_index: int = 0):
    """
    Complete workflow: Parse -> Render -> Play

    Args:
        file_path:   Path to .gp3/.gp4/.gp5/.gpx file
        track_index: Track index to process (default: first track)
    """
    # Step 1: Parse
    print(f"[1/3] Parsing: {file_path}")
    song = parse_gtp(file_path)
    print(f"  OK Title: {song.title}, BPM: {song.tempo}")

    # Step 2: Render
    print("[2/3] Rendering tablature...")
    renderer = TabRenderer(RenderConfig())
    pages = renderer.render(song, track_index=track_index)

    for i, page in enumerate(pages):
        output_file = f"{song.title}_track{track_index}_p{i + 1}.png"
        page.save(output_file)
        print(f"  OK Saved: {output_file} ({page.width()}x{page.height()}px)")

    # Step 3: Play
    print("[3/3] Initializing audio...")
    converter = MidiConverter()
    events = converter.convert(song, track_index=track_index)

    engine = SynthEngine()
    engine.initialize()
    engine.load_soundfont()
    engine.set_instrument(0, 27)
    engine.load_events(events, bpm=song.tempo)

    print("  > Playing (Ctrl+C to stop)...")
    engine.play()

    try:
        while engine.is_playing:
            time.sleep(0.1)
    except KeyboardInterrupt:
        print("\n  [] Stopped")
        engine.stop()

if __name__ == "__main__":
    import sys

    if len(sys.argv) < 2:
        print("Usage: python example.py <file_path.gp5> [track_index]")
        sys.exit(1)

    file_path = sys.argv[1]
    track_index = int(sys.argv[2]) if len(sys.argv) > 2 else 0

    process_gtp_file(file_path, track_index)

API Reference

Core Functions

Function Description Return Value
parse_gtp(path) Parse GTP file GTPSong
render_gtp(path, track_index) One-click render GTP file List[QPixmap]

Main Classes

Data Models (ApolloTab.models)

Class Description
GTPSong Song object (title/artist/BPM/tracks list)
GTPTrack Track object (name/tuning/measures list)
GTPMeasure Measure object (time signature/repeat marks/beats list)
GTPBeat Beat object (duration/dot/notes list)
GTPNote Note object (fret/string/MIDI pitch/techniques)

Parser (ApolloTab.parser)

Class Description
GTPParser GTP file parser class

Usage:

from ApolloTab.parser import GTPParser

parser = GTPParser()
song = parser.parse("song.gp5")

Renderer (ApolloTab.renderer)

Class Description
TabRenderer Tablature rendering engine
TabLayoutEngine Layout calculation engine

Usage:

from ApolloTab.renderer import TabRenderer, RenderConfig

renderer = TabRenderer(RenderConfig())
pages = renderer.render(song, track_index=0)

Audio Engine (ApolloTab.audio)

Class Description
MidiConverter GTP data to MIDI event converter
SynthEngine FluidSynth audio synthesis engine
MidiEvent Single MIDI event data model

Usage:

from ApolloTab.audio import MidiConverter, SynthEngine

converter = MidiConverter()
events = converter.convert(song, track_index=0)

engine = SynthEngine()
engine.initialize()
engine.load_soundfont()
engine.load_events(events, bpm=120)
engine.play()

Utilities (ApolloTab.utils)

Class / Constant Description
RenderConfig Rendering parameter configuration (all adjustable)
TechniqueType Technique type enum (18 types)
StandardTunings Standard tuning definitions
NoteDuration Duration enum

Configuration

RenderConfig Parameters

config = RenderConfig(
    # Page size
    page_width=2480,       # Page width(px), effect: larger = sharper but more memory
    page_height=3508,      # Page height(px), A4@300dpi standard size

    # Margins
    margin_top=80,         # Top margin(px), effect: larger = content shifts down
    margin_bottom=60,      # Bottom margin(px)
    margin_left=60,        # Left margin(px)
    margin_right=60,       # Right margin(px)

    # Tablature style
    string_spacing=12,     # String line spacing(px), effect: larger = wider, more readable
    line_width=1,          # Line thickness(px), effect: 0.5=thin, 2=thick
    line_color="#333333",  # Line color(hex), effect: changes overall tone

    # Font settings
    font_family="Arial",   # Font family, effect: use system-supported font name
    font_size_fret=10,     # Fret number font size(px), effect: larger = clearer numbers
    font_size_technique=9, # Technique label font size(px),

    # System spacing
    system_spacing=40,     # System(row) spacing(px), effect: larger = more whitespace between rows
)

SynthEngine Audio Parameters

engine = SynthEngine(
    sample_rate=44100,     # Sample rate(Hz), effect: 48000 = clearer but higher CPU usage
    buffer_size=512,       # Buffer size, effect: 256 = lower latency but may cause audio glitches
    gain=0.8,              # Master volume(0.0-1.0), effect: 1.0 = max volume
)

Supported Techniques

Technique Abbreviation Symbol Type
Hammer-On H Text label
Pull-Off P Text label
Slide Up s/S Line + arrow
Slide Down S Line + arrow
Bend B Arc + arrow + degree
Vibrato ~ Wavy line
Palm Mute P.M. Dashed extension
Staccato . Dot mark
Let Ring Dashed extension
Natural Harmonic N.H. Diamond mark
Artificial Harmonic A.H. Diamond + text
Tremolo Picking Trem.Pick. Diagonal underline
Trill "tr" text
Grace Note Small note
Accentuated > Symbol
Ghost Note Parentheses

Development Guide

Project Structure

ApolloTab/
├── __init__.py           # Package entry, exports core API
├── py.typed              # PEP 561 type hint marker
├── parser/
│   ├── __init__.py
│   └── gtp_parser.py     # PyGuitarPro -> GTPSong conversion
├── models/
│   ├── __init__.py
│   ├── song.py           # Song model
│   ├── track.py          # Track model
│   ├── measure.py        # Measure model
│   ├── beat.py           # Beat model
│   └── note.py           # Note model
├── renderer/
│   ├── __init__.py
│   ├── tab_renderer.py   # Tablature drawing engine
│   └── layout_engine.py  # Coordinate layout calculation
├── audio/
│   ├── __init__.py
│   ├── midi_converter.py # GTP -> MIDI event conversion
│   └── synth_engine.py   # FluidSynth synthesis engine
└── utils/
    ├── __init__.py
    └── constants.py      # Global constant definitions

Local Development

git clone https://github.com/your-repo/ApolloTab.git
cd ApolloTab

python -m venv venv
source venv/bin/activate  # Linux/Mac
# or .\venv\Scripts\activate  # Windows

pip install -e ".[dev]"

pytest tests/ -v

black ApolloTab/
isort ApolloTab/

mypy ApolloTab/

Publish to PyPI

python -m build
twine check dist/*
twine upload --repository testpypi dist/*
twine upload dist/*

Dependencies

Required Dependencies

Package Version Purpose License
pyguitarpro >=0.11 Guitar Pro file parsing LGPL-2.1
PyQt5 >=5.15 GUI rendering framework GPL v3
pyfluidsynth >=1.4.0 FluidSynth Python binding LGPL-2.1

Optional Dependencies

Group Package Purpose
dev pytest, black, isort, mypy Development and testing tools

License

This project is licensed under the MPL 2.0.

Contributing

Issues and Pull Requests are welcome!

  1. Fork this repository
  2. Create a feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Submit a Pull Request

Related Projects


Version: v0.2.2 Last Updated: 2026-06-12 Compatibility: Windows / Linux / macOS (Python 3.8+)



ApolloTab(中文)

Python 3.8+ License: MPL 2.0 PyPI version

Guitar Pro 文件解析、渲染与音频播放引擎库

ApolloTab 是一个功能完整的 Python 库,用于解析、渲染和播放 Guitar Pro (.gp3/.gp4/.gp5/.gpx) 格式的吉他谱文件。

核心功能

  • 文件解析: 完整支持 GP3/GP4/GP5/GPX 格式,提取歌曲信息、音轨、小节、音符、技巧标记
  • 六线谱渲染: 使用 QPainter 将乐谱数据渲染为高质量六线谱图像(QPixmap),支持多页输出
  • 音频播放: 基于 FluidSynth 的 MIDI 合成引擎,支持 SoundFont 音色库实时播放
  • 技巧支持: 18种演奏技巧(击弦、勾弦、推弦、滑音、泛音、颤音等)
  • 高度可配置: 渲染参数完全可调(线宽、间距、颜色、字体等)

安装

从 PyPI 安装(推荐)

pip install ApolloTab

从源码安装

# 克隆仓库
git clone https://github.com/your-repo/ApolloTab.git
cd ApolloTab

# 安装依赖
pip install -e .

# 或安装开发环境(含测试工具)
pip install -e ".[dev]"

快速开始

1. 解析 GTP 文件

from ApolloTab import parse_gtp

# 解析 Guitar Pro 文件
song = parse_gtp("my_song.gp5")

# 查看基本信息
print(f"标题: {song.title}")
print(f"艺术家: {song.artist}")
print(f"BPM: {song.tempo}")
print(f"音轨数: {song.track_count}")

# 遍历音轨
for track in song.tracks:
    print(f"\n音轨: {track.name}")
    print(f"调弦: {track.strings}")
    print(f"小节数: {len(track.measures)}")

2. 渲染六线谱图像

from ApolloTab import render_gtp

# 一键渲染(返回多页 QPixmap 列表)
pages = render_gtp("my_song.gp5", track_index=0)

# pages 是 List[QPixmap],每页一张图片
for i, page in enumerate(pages):
    page.save(f"output_page_{i + 1}.png")
    print(f"已保存第 {i + 1} 页")

# 或者使用 TabRenderer 类进行更精细的控制
from ApolloTab import TabRenderer, RenderConfig

# 自定义渲染配置
config = RenderConfig(
    page_width=2480,      # 页面宽度(px),调整效果: 越大越清晰但内存占用更多
    page_height=3508,     # 页面高度(px),A4@300dpi标准尺寸
    line_color="#000000", # 弦线颜色
)

renderer = TabRenderer(config=config)
pages = renderer.render(song, track_index=0)

# 访问布局数据(用于播放光标等功能)
layouts = renderer.last_layouts  # List[PageLayout]

3. 音频播放

from ApolloTab import (
    parse_gtp,
    MidiConverter,
    SynthEngine,
)

# 解析文件
song = parse_gtp("my_song.gp5")

# 转换为 MIDI 事件序列
converter = MidiConverter()
events = converter.convert(song, track_index=0)

# 初始化音频引擎
engine = SynthEngine()
engine.initialize()                    # 初始化 FluidSynth 合成器
engine.load_soundfont()               # 自动搜索并加载 SoundFont
engine.set_instrument(0, 27)          # 设置通道0为电吉他(MIDI程序号27)

# 加载事件并播放
engine.load_events(events, bpm=song.tempo)
engine.play()

# 控制播放
import time
time.sleep(5)  # 播放5秒
engine.pause()
time.sleep(2)  # 暂停2秒
engine.resume()
time.sleep(5)  # 继续播放5秒

# 停止并清理
engine.stop()

4. 完整示例:解析 → 渲染 → 播放

from ApolloTab import (
    parse_gtp,
    TabRenderer,
    MidiConverter,
    SynthEngine,
    RenderConfig,
)

def process_gtp_file(file_path: str, track_index: int = 0):
    """
    完整处理流程:解析 → 渲染 → 播放
    
    参数:
        file_path:   .gp3/.gp4/.gp5/.gpx 文件路径
        track_index: 要处理的音轨索引(默认第1条)
    """
    # ===== 步骤1: 解析 =====
    print(f"[1/3] 正在解析: {file_path}")
    song = parse_gtp(file_path)
    print(f"  ✓ 标题: {song.title}, BPM: {song.tempo}")
    
    # ===== 步骤2: 渲染 =====
    print("[2/3] 正在渲染六线谱...")
    renderer = TabRenderer(RenderConfig())
    pages = renderer.render(song, track_index=track_index)
    
    for i, page in enumerate(pages):
        output_file = f"{song.title}_track{track_index}_p{i + 1}.png"
        page.save(output_file)
        print(f"  ✓ 已保存: {output_file} ({page.width()}x{page.height()}px)")
    
    # ===== 步骤3: 播放 =====
    print("[3/3] 正在初始化音频...")
    converter = MidiConverter()
    events = converter.convert(song, track_index=track_index)
    
    engine = SynthEngine()
    engine.initialize()
    engine.load_soundfont()
    engine.set_instrument(0, 27)
    engine.load_events(events, bpm=song.tempo)
    
    print("  ▶ 开始播放 (按 Ctrl+C 停止)...")
    engine.play()
    
    try:
        while engine.is_playing:
            time.sleep(0.1)
    except KeyboardInterrupt:
        print("\n  ⏹ 停止播放")
        engine.stop()

# 使用示例
if __name__ == "__main__":
    import sys
    
    if len(sys.argv) < 2:
        print("用法: python example.py <文件路径.gp5> [音轨索引]")
        sys.exit(1)
    
    file_path = sys.argv[1]
    track_index = int(sys.argv[2]) if len(sys.argv) > 2 else 0
    
    process_gtp_file(file_path, track_index)

API 参考

核心函数

函数 说明 返回值
parse_gtp(path) 解析GTP文件 GTPSong
render_gtp(path, track_index) 一键渲染GTP文件 List[QPixmap]

主要类

数据模型 (ApolloTab.models)

类名 说明
GTPSong 歌曲对象(标题/艺术家/BPM/音轨列表)
GTPTrack 音轨对象(名称/调弦/小节列表)
GTPMeasure 小节对象(拍号/重复记号/拍列表)
GTPBeat 拍对象(时值/附点/音符列表)
GTPNote 音符对象(品格/弦/MIDI音高/技巧)

解析器 (ApolloTab.parser)

类名 说明
GTPParser GTP文件解析器类

用法:

from ApolloTab.parser import GTPParser

parser = GTPParser()
song = parser.parse("song.gp5")

渲染器 (ApolloTab.renderer)

类名 说明
TabRenderer 六线谱渲染引擎
TabLayoutEngine 布局计算引擎

用法:

from ApolloTab.renderer import TabRenderer, RenderConfig

renderer = TabRenderer(RenderConfig())
pages = renderer.render(song, track_index=0)

音频引擎 (ApolloTab.audio)

类名 说明
MidiConverter GTP数据→MIDI事件转换器
SynthEngine FluidSynth音频合成引擎
MidiEvent 单个MIDI事件数据模型

用法:

from ApolloTab.audio import MidiConverter, SynthEngine

converter = MidiConverter()
events = converter.convert(song, track_index=0)

engine = SynthEngine()
engine.initialize()
engine.load_soundfont()
engine.load_events(events, bpm=120)
engine.play()

工具 (ApolloTab.utils)

类/常量 说明
RenderConfig 渲染参数配置(全部可调)
TechniqueType 技巧类型枚举(18种)
StandardTunings 标准调弦定义
NoteDuration 时值枚举

配置说明

RenderConfig 渲染参数

config = RenderConfig(
    # 页面尺寸
    page_width=2480,       # 页面宽度(px), 调整效果: A4@300dpi=2480, 屏幕显示可用1200
    page_height=3508,      # 页面高度(px), 调整效果: A4@3508, 可根据需要调整
    
    # 边距
    margin_top=80,         # 上边距(px), 调整效果: 增大则内容下移
    margin_bottom=60,      # 下边距(px)
    margin_left=60,        # 左边距(px)
    margin_right=60,       # 右边距(px)
    
    # 六线谱样式
    string_spacing=12,     # 弦线间距(px), 调整效果: 增大则谱子更宽更易读
    line_width=1,          # 弦线粗细(px), 调整效果: 0.5=细线, 2=粗线
    line_color="#333333",  # 弦线颜色(十六进制), 调整效果: 改变整体色调
    
    # 字体设置
    font_family="Arial",   # 字体族, 调整效果: 使用系统支持的字体的名称
    font_size_fret=10,     # 品格数字大小(px), 调整效果: 增大则数字更清晰
    font_size_technique=9, # 技巧标记大小(px),
    
    # 系统间距
    system_spacing=40,     # 系统(行)间距(px), 调整效果: 增大则行间空白更多
)

SynthEngine 音频参数

engine = SynthEngine(
    sample_rate=44100,     # 采样率(Hz), 调整效果: 48000更清晰但CPU占用更高
    buffer_size=512,       # 缓冲区大小, 调整效果: 256延迟更低但可能爆音
    gain=0.8,              # 主音量(0.0-1.0), 调整效果: 1.0=最大音量
)

支持的演奏技巧

技巧 缩写 符号类型
击弦 Hammer-On H 文字标签
勾弦 Pull-Off P 文字标签
上滑音 Slide Up s/S 连线+箭头
下滑音 Slide Down S 连线+箭头
推弦 Bend B 弧线+箭头+度数
颤音 Vibrato ~ 波浪线
闷音 Palm Mute P.M. 虚线延长线
断奏 Staccato . 点标记
延音 Let Ring 虚线延长线
自然泛音 N.H. 菱形标记
人工泛音 A.H. 菱形+文字
震音拨弦 Trem.Pick. 斜线下划线
颤音 Trill "tr"文字
装饰音 Grace Note 小音符
重音 Accentuated > 符号
幽灵音 Ghost Note 括号包裹

开发指南

项目结构

ApolloTab/
├── __init__.py           # 包入口,导出核心API
├── py.typed              # PEP 561 类型提示标记
├── parser/
│   ├── __init__.py
│   └── gtp_parser.py     # PyGuitarPro → GTPSong 转换
├── models/
│   ├── __init__.py
│   ├── song.py           # 歌曲模型
│   ├── track.py          # 音轨模型
│   ├── measure.py        # 小节模型
│   ├── beat.py           # 拍模型
│   └── note.py           # 音符模型
├── renderer/
│   ├── __init__.py
│   ├── tab_renderer.py   # 六线谱绘制引擎
│   └── layout_engine.py  # 坐标布局计算
├── audio/
│   ├── __init__.py
│   ├── midi_converter.py # GTP → MIDI事件转换
│   └── synth_engine.py   # FluidSynth合成引擎
└── utils/
    ├── __init__.py
    └── constants.py      # 全局常量定义

本地开发

# 克隆仓库
git clone https://github.com/your-repo/ApolloTab.git
cd ApolloTab

# 创建虚拟环境
python -m venv venv
source venv/bin/activate  # Linux/Mac
# 或 .\venv\Scripts\activate  # Windows

# 安装开发依赖
pip install -e ".[dev]"

# 运行测试
pytest tests/ -v

# 代码格式化
black ApolloTab/
isort ApolloTab/

# 类型检查
mypy ApolloTab/

发布到 PyPI

# 1. 构建分发包
python -m build

# 2. 检查包内容
twine check dist/*

# 3. 发布到 TestPyPI(测试)
twine upload --repository testpypi dist/*

# 4. 发布到正式 PyPI
twine upload dist/*

依赖项

必需依赖

包名 版本 用途 许可证
pyguitarpro >=0.11 Guitar Pro文件解析 LGPL-2.1
PyQt5 >=5.15 GUI渲染框架 GPL v3
pyfluidsynth >=1.4.0 FluidSynth Python绑定 LGPL-2.1

可选依赖

组名 包名 用途
dev pytest, black, isort, mypy 开发和测试工具

许可证

本项目采用 MPL 2.0 开源协议。

贡献

欢迎提交 Issue 和 Pull Request!

  1. Fork 本仓库
  2. 创建特性分支 (git checkout -b feature/AmazingFeature)
  3. 提交更改 (git commit -m 'Add some AmazingFeature')
  4. 推送到分支 (git push origin feature/AmazingFeature)
  5. 提交 Pull Request

相关项目


版本: v0.2.2 最后更新: 2026-06-12 兼容性: Windows / Linux / macOS (Python 3.8+)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

apollotab-0.2.2.tar.gz (5.9 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

apollotab-0.2.2-py3-none-any.whl (6.9 MB view details)

Uploaded Python 3

File details

Details for the file apollotab-0.2.2.tar.gz.

File metadata

  • Download URL: apollotab-0.2.2.tar.gz
  • Upload date:
  • Size: 5.9 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for apollotab-0.2.2.tar.gz
Algorithm Hash digest
SHA256 abfafffd79709517b8ad8b3054559ecf257bcfc9f7c78e2495cf3c9ec6752948
MD5 7877abbec3d29973d9963d27dfd6f888
BLAKE2b-256 6199af2e49ead992931604615dfae9b503c710c406926ca4cc9afadaa689cc9e

See more details on using hashes here.

File details

Details for the file apollotab-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: apollotab-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 6.9 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for apollotab-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 1b4dd8d4ead165f81e585a692a42937cd8285aa8f3985045a6cfb595e6a9d49b
MD5 db7663a3c38ee71fe1a94e26a0b2ca05
BLAKE2b-256 8c10e39529889c8302bc43e4dcd9269dc46b9df1564158c1357fad5ba61df8fd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page