forked from GithubBackups/healthchecks
Keep a log of pings
This commit is contained in:
parent
d8828b2d25
commit
3550218129
@ -1,16 +1,18 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from hc.api.models import Check
|
from hc.api.models import Check, Ping
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Check)
|
@admin.register(Check)
|
||||||
class ChecksAdmin(admin.ModelAdmin):
|
class ChecksAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
css = {
|
css = {
|
||||||
'all': ('css/admin/checks.css',)
|
'all': ('css/admin/checks.css',)
|
||||||
}
|
}
|
||||||
|
|
||||||
list_display = ("id", "name", "created", "code", "status", "email", "last_ping")
|
list_display = ("id", "name", "created", "code", "status", "email",
|
||||||
|
"last_ping")
|
||||||
list_select_related = ("user", )
|
list_select_related = ("user", )
|
||||||
actions = ["send_alert"]
|
actions = ["send_alert"]
|
||||||
|
|
||||||
@ -24,3 +26,8 @@ class ChecksAdmin(admin.ModelAdmin):
|
|||||||
self.message_user(request, "%d alert(s) sent" % qs.count())
|
self.message_user(request, "%d alert(s) sent" % qs.count())
|
||||||
|
|
||||||
send_alert.short_description = "Send Alert"
|
send_alert.short_description = "Send Alert"
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Ping)
|
||||||
|
class PingsAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ("id", "created", "owner", "method", "ua")
|
||||||
|
26
hc/api/migrations/0007_ping.py
Normal file
26
hc/api/migrations/0007_ping.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('api', '0006_check_grace'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Ping',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('remote_addr', models.GenericIPAddressField()),
|
||||||
|
('method', models.CharField(max_length=10)),
|
||||||
|
('ua', models.CharField(max_length=100, blank=True)),
|
||||||
|
('body', models.TextField(blank=True)),
|
||||||
|
('owner', models.ForeignKey(to='api.Check')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
@ -1,3 +1,5 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
from datetime import timedelta as td
|
from datetime import timedelta as td
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
@ -24,6 +26,9 @@ class Check(models.Model):
|
|||||||
alert_after = models.DateTimeField(null=True, blank=True, editable=False)
|
alert_after = models.DateTimeField(null=True, blank=True, editable=False)
|
||||||
status = models.CharField(max_length=6, choices=STATUSES, default="new")
|
status = models.CharField(max_length=6, choices=STATUSES, default="new")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Check(%s)" % self.code
|
||||||
|
|
||||||
def url(self):
|
def url(self):
|
||||||
return settings.PING_ENDPOINT + str(self.code)
|
return settings.PING_ENDPOINT + str(self.code)
|
||||||
|
|
||||||
@ -53,3 +58,12 @@ class Check(models.Model):
|
|||||||
return "grace"
|
return "grace"
|
||||||
|
|
||||||
return "down"
|
return "down"
|
||||||
|
|
||||||
|
|
||||||
|
class Ping(models.Model):
|
||||||
|
owner = models.ForeignKey(Check)
|
||||||
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
remote_addr = models.GenericIPAddressField()
|
||||||
|
method = models.CharField(max_length=10)
|
||||||
|
ua = models.CharField(max_length=100, blank=True)
|
||||||
|
body = models.TextField(blank=True)
|
||||||
|
@ -5,7 +5,7 @@ from django.http import HttpResponse, HttpResponseBadRequest
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
from hc.api.models import Check
|
from hc.api.models import Check, Ping
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@ -21,6 +21,14 @@ def ping(request, code):
|
|||||||
|
|
||||||
check.save()
|
check.save()
|
||||||
|
|
||||||
|
ping = Ping(owner=check)
|
||||||
|
headers = request.META
|
||||||
|
ping.remote_addr = headers.get("X_REAL_IP", headers["REMOTE_ADDR"])
|
||||||
|
ping.method = headers["REQUEST_METHOD"]
|
||||||
|
ping.ua = headers.get("HTTP_USER_AGENT", "")
|
||||||
|
ping.body = request.body
|
||||||
|
ping.save()
|
||||||
|
|
||||||
response = HttpResponse("OK")
|
response = HttpResponse("OK")
|
||||||
response["Access-Control-Allow-Origin"] = "*"
|
response["Access-Control-Allow-Origin"] = "*"
|
||||||
return response
|
return response
|
||||||
|
@ -3,13 +3,14 @@ from django.conf.urls import url
|
|||||||
from hc.front import views
|
from hc.front import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', views.index, name="hc-index"),
|
url(r'^$', views.index, name="hc-index"),
|
||||||
url(r'^checks/add/$', views.add_check, name="hc-add-check"),
|
url(r'^checks/add/$', views.add_check, name="hc-add-check"),
|
||||||
url(r'^checks/([\w-]+)/name/$', views.update_name, name="hc-update-name"),
|
url(r'^checks/([\w-]+)/name/$', views.update_name, name="hc-update-name"),
|
||||||
url(r'^checks/([\w-]+)/timeout/$', views.update_timeout, name="hc-update-timeout"),
|
url(r'^checks/([\w-]+)/timeout/$', views.update_timeout, name="hc-update-timeout"),
|
||||||
url(r'^checks/([\w-]+)/email/$', views.email_preview),
|
url(r'^checks/([\w-]+)/email/$', views.email_preview),
|
||||||
url(r'^checks/([\w-]+)/remove/$', views.remove, name="hc-remove-check"),
|
url(r'^checks/([\w-]+)/remove/$', views.remove, name="hc-remove-check"),
|
||||||
url(r'^pricing/$', views.pricing, name="hc-pricing"),
|
url(r'^checks/([\w-]+)/log/$', views.log, name="hc-log"),
|
||||||
url(r'^docs/$', views.docs, name="hc-docs"),
|
url(r'^pricing/$', views.pricing, name="hc-pricing"),
|
||||||
url(r'^about/$', views.about, name="hc-about"),
|
url(r'^docs/$', views.docs, name="hc-docs"),
|
||||||
|
url(r'^about/$', views.about, name="hc-about"),
|
||||||
]
|
]
|
||||||
|
@ -6,7 +6,7 @@ from django.http import HttpResponseForbidden
|
|||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from hc.api.models import Check
|
from hc.api.models import Check, Ping
|
||||||
from hc.front.forms import TimeoutForm
|
from hc.front.forms import TimeoutForm
|
||||||
|
|
||||||
|
|
||||||
@ -147,3 +147,21 @@ def remove(request, code):
|
|||||||
check.delete()
|
check.delete()
|
||||||
|
|
||||||
return redirect("hc-index")
|
return redirect("hc-index")
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def log(request, code):
|
||||||
|
|
||||||
|
check = Check.objects.get(code=code)
|
||||||
|
if check.user != request.user:
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
|
pings = Ping.objects.filter(owner=check).order_by("-created")[:100]
|
||||||
|
|
||||||
|
ctx = {
|
||||||
|
"check": check,
|
||||||
|
"pings": pings
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, "front/log.html", ctx)
|
||||||
|
@ -131,7 +131,7 @@ table.table tr > th.th-name {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.my-checks-name:hover {
|
#checks-table tr:hover .my-checks-name {
|
||||||
border: 1px dotted #AAA;
|
border: 1px dotted #AAA;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +142,7 @@ table.table tr > th.th-name {
|
|||||||
|
|
||||||
.url-cell {
|
.url-cell {
|
||||||
font-size: small;
|
font-size: small;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
td.inactive .popover {
|
td.inactive .popover {
|
||||||
@ -167,7 +168,7 @@ td.inactive .popover {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeout_grace:hover {
|
#checks-table tr:hover .timeout_grace {
|
||||||
border: 1px dotted #AAA;
|
border: 1px dotted #AAA;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,3 +244,8 @@ tr:hover .check-menu {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Log */
|
||||||
|
|
||||||
|
.log-table .remote-addr, .log-table .ua {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
42
templates/front/log.html
Normal file
42
templates/front/log.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load compress humanize staticfiles hc_extras %}
|
||||||
|
|
||||||
|
{% block title %}My Checks - healthchecks.io{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<h1>Log for {{ check.name|default:check.code }}</h1>
|
||||||
|
{% if pings %}
|
||||||
|
<table class="table table-striped log-table">
|
||||||
|
<tr>
|
||||||
|
<th>Time</th>
|
||||||
|
<th>Remote IP</th>
|
||||||
|
<th>Method</th>
|
||||||
|
<th>User Agent</th>
|
||||||
|
</tr>
|
||||||
|
{% for ping in pings %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
data-toggle="tooltip"
|
||||||
|
title="{{ ping.created }}">
|
||||||
|
{{ ping.created }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="remote-addr">{{ ping.remote_addr }}</td>
|
||||||
|
<td class="method">
|
||||||
|
<span class="label label-default">{{ ping.method }}</span>
|
||||||
|
</td>
|
||||||
|
<td class="ua">{{ ping.ua }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<div class="alert alert-info">Log is empty. This check has not received any pings yet.</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -75,6 +75,13 @@
|
|||||||
<span class="glyphicon glyphicon-cog" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-cog" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'hc-log' check.code %}">
|
||||||
|
<span class="glyphicon glyphicon-time"></span>
|
||||||
|
Log
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li role="separator" class="divider"></li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#" class="check-menu-remove"
|
<a href="#" class="check-menu-remove"
|
||||||
data-name="{{ check.name|default:check.code }}"
|
data-name="{{ check.name|default:check.code }}"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user