mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-21 05:45:13 +00:00
Merge branch 'sictiru' of github.com:samuelclay/NewsBlur into sictiru
This commit is contained in:
commit
b5040d8134
36 changed files with 839 additions and 265 deletions
|
@ -133,10 +133,16 @@ You got the downtime message either through email or SMS. This is the order of o
|
||||||
When the new redis server is connected to the primary redis server:
|
When the new redis server is connected to the primary redis server:
|
||||||
|
|
||||||
# db-redis-story2 = moving to new server
|
# db-redis-story2 = moving to new server
|
||||||
# db-redis-story = old server about to be shutdown
|
# db-redis-story1 = old server about to be shutdown
|
||||||
|
# Edit digitalocean.tf to change db-redis-story count to 2
|
||||||
|
make plan
|
||||||
|
make apply
|
||||||
|
make firewall
|
||||||
|
# Wait for redis to sync, takes 5-10 minutes
|
||||||
|
# Edit redis/consul_service.json to switch primary to db-redis-story2
|
||||||
make celery_stop
|
make celery_stop
|
||||||
make maintenance_on
|
make maintenance_on
|
||||||
apd -l db-redis-story2 -t replicaofnoone
|
apd -l db-redis-story2 -t replicaofnoone
|
||||||
aps -l db-redis-story,db-redis-story2 -t consul
|
aps -l db-redis-story1,db-redis-story2 -t consul
|
||||||
make maintenance_off
|
make maintenance_off
|
||||||
make task
|
make task
|
||||||
|
|
|
@ -16,6 +16,9 @@ groups:
|
||||||
node: inventory_hostname.startswith('node')
|
node: inventory_hostname.startswith('node')
|
||||||
node_socket: inventory_hostname.startswith('node-socket')
|
node_socket: inventory_hostname.startswith('node-socket')
|
||||||
node_images: inventory_hostname.startswith('node-images')
|
node_images: inventory_hostname.startswith('node-images')
|
||||||
|
node_text: inventory_hostname.startswith('node-text')
|
||||||
|
node_page: inventory_hostname.startswith('node-page')
|
||||||
|
node_favicons: inventory_hostname.startswith('node-favicons')
|
||||||
|
|
||||||
# debugs: inventory_hostname.startswith('debug')
|
# debugs: inventory_hostname.startswith('debug')
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
],
|
],
|
||||||
"checks": [{
|
"checks": [{
|
||||||
"id": "consul-manager",
|
"id": "consul-manager",
|
||||||
"http": "http://{{ ansible_ssh_host }}:8500",
|
"http": "http://{{ ansible_host }}:8500",
|
||||||
"interval": "15s",
|
"interval": "15s",
|
||||||
"failures_before_critical": 4
|
"failures_before_critical": 4
|
||||||
}],
|
}],
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"port": 9200,
|
"port": 9200,
|
||||||
"checks": [{
|
"checks": [{
|
||||||
"id": "es-ping",
|
"id": "es-ping",
|
||||||
"http": "http://{{ ansible_ssh_host }}:5579/db_check/elasticsearch",
|
"http": "http://{{ ansible_host }}:5579/db_check/elasticsearch",
|
||||||
"interval": "15s",
|
"interval": "15s",
|
||||||
"failures_before_critical": 4
|
"failures_before_critical": 4
|
||||||
}]
|
}]
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
],
|
],
|
||||||
"checks": [{
|
"checks": [{
|
||||||
"id": "grafana-ping",
|
"id": "grafana-ping",
|
||||||
"http": "http://{{ ansible_ssh_host }}:3000/api/health",
|
"http": "http://{{ ansible_host }}:3000/api/health",
|
||||||
"interval": "15s",
|
"interval": "15s",
|
||||||
"failures_before_critical": 4
|
"failures_before_critical": 4
|
||||||
}],
|
}],
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
"checks": [
|
"checks": [
|
||||||
{
|
{
|
||||||
"id": "{{inventory_hostname}}-exporter-ping",
|
"id": "{{inventory_hostname}}-exporter-ping",
|
||||||
"http": "http://{{ ansible_ssh_host }}:9216",
|
"http": "http://{{ ansible_host }}:9216",
|
||||||
"interval": "15s",
|
"interval": "15s",
|
||||||
"failures_before_critical": 4
|
"failures_before_critical": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
"port": 27017,
|
"port": 27017,
|
||||||
"checks": [{
|
"checks": [{
|
||||||
"id": "mongo-analytics-ping",
|
"id": "mongo-analytics-ping",
|
||||||
"http": "http://{{ ansible_ssh_host }}:5579/db_check/mongo_analytics?consul=1",
|
"http": "http://{{ ansible_host }}:5579/db_check/mongo_analytics?consul=1",
|
||||||
"interval": "15s"
|
"interval": "15s"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
"port": 27017,
|
"port": 27017,
|
||||||
"checks": [{
|
"checks": [{
|
||||||
"id": "mongo-ping",
|
"id": "mongo-ping",
|
||||||
"http": "http://{{ ansible_ssh_host }}:5579/db_check/mongo?consul=1",
|
"http": "http://{{ ansible_host }}:5579/db_check/mongo?consul=1",
|
||||||
"interval": "15s",
|
"interval": "15s",
|
||||||
"failures_before_critical": 4
|
"failures_before_critical": 4
|
||||||
}]
|
}]
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
"checks": [
|
"checks": [
|
||||||
{
|
{
|
||||||
"id": "{{inventory_hostname}}-node-exporter-ping",
|
"id": "{{inventory_hostname}}-node-exporter-ping",
|
||||||
"http": "http://{{ ansible_ssh_host }}:9100",
|
"http": "http://{{ ansible_host }}:9100",
|
||||||
"interval": "15s",
|
"interval": "15s",
|
||||||
"failures_before_critical": 4
|
"failures_before_critical": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,13 @@
|
||||||
"checks": [{
|
"checks": [{
|
||||||
"id": "{{inventory_hostname}}-ping",
|
"id": "{{inventory_hostname}}-ping",
|
||||||
{% if item.target_host == "node-images" %}
|
{% if item.target_host == "node-images" %}
|
||||||
"http": "http://{{ ansible_ssh_host }}:{{ item.port }}/sc,seLJDaKBog3LLEMDe8cjBefMhnVSibO4RA5boZhWcVZ0=/https://samuelclay.com/static/images/2019%20-%20Cuba.jpg",
|
"http": "http://{{ ansible_host }}:{{ item.port }}/sc,seLJDaKBog3LLEMDe8cjBefMhnVSibO4RA5boZhWcVZ0=/https://samuelclay.com/static/images/2019%20-%20Cuba.jpg",
|
||||||
{% elif item.target_host == "node-favicons" %}
|
{% elif item.target_host == "node-favicons" %}
|
||||||
"http": "http://{{ ansible_ssh_host }}:{{ item.port }}/rss_feeds/icon/1",
|
"http": "http://{{ ansible_host }}:{{ item.port }}/rss_feeds/icon/1",
|
||||||
{% elif item.target_host == "node-text" %}
|
{% elif item.target_host == "node-text" %}
|
||||||
"http": "http://{{ ansible_ssh_host }}:{{ item.port }}/rss_feeds/original_text_fetcher?test=1",
|
"http": "http://{{ ansible_host }}:{{ item.port }}/rss_feeds/original_text_fetcher?test=1",
|
||||||
{% elif item.target_host == "node-page" %}
|
{% elif item.target_host == "node-page" %}
|
||||||
"http": "http://{{ ansible_ssh_host }}:{{ item.port }}/original_page/1?test=1",
|
"http": "http://{{ ansible_host }}:{{ item.port }}/original_page/1?test=1",
|
||||||
{% endif %}
|
{% endif %}
|
||||||
"interval": "15s"
|
"interval": "15s"
|
||||||
}]
|
}]
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
"checks": [
|
"checks": [
|
||||||
{
|
{
|
||||||
"id": "{{inventory_hostname}}-exporter-ping",
|
"id": "{{inventory_hostname}}-exporter-ping",
|
||||||
"http": "http://{{ ansible_ssh_host }}:9187",
|
"http": "http://{{ ansible_host }}:9187",
|
||||||
"interval": "15s",
|
"interval": "15s",
|
||||||
"failures_before_critical": 4
|
"failures_before_critical": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
"port": 5432,
|
"port": 5432,
|
||||||
"checks": [{
|
"checks": [{
|
||||||
"id": "postgres-ping",
|
"id": "postgres-ping",
|
||||||
"http": "http://{{ ansible_ssh_host }}:5579/db_check/postgres?consul=1",
|
"http": "http://{{ ansible_host }}:5579/db_check/postgres?consul=1",
|
||||||
"interval": "15s",
|
"interval": "15s",
|
||||||
"failures_before_critical": 4
|
"failures_before_critical": 4
|
||||||
}]
|
}]
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
],
|
],
|
||||||
"checks": [{
|
"checks": [{
|
||||||
"id": "prometheus-ping",
|
"id": "prometheus-ping",
|
||||||
"http": "http://{{ ansible_ssh_host }}:9090/metrics",
|
"http": "http://{{ ansible_host }}:9090/metrics",
|
||||||
"interval": "15s",
|
"interval": "15s",
|
||||||
"failures_before_critical": 4
|
"failures_before_critical": 4
|
||||||
}],
|
}],
|
||||||
"port": 9090
|
"port": 9090
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
"checks": [
|
"checks": [
|
||||||
{
|
{
|
||||||
"id": "{{ item.redis_target }}-exporter-ping",
|
"id": "{{ item.redis_target }}-exporter-ping",
|
||||||
"http": "http://{{ ansible_ssh_host }}:{{ item.port }}",
|
"http": "http://{{ ansible_host }}:{{ item.port }}",
|
||||||
"interval": "15s",
|
"interval": "15s",
|
||||||
"failures_before_critical": 4
|
"failures_before_critical": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,13 @@
|
||||||
become: yes
|
become: yes
|
||||||
sysctl: name=vm.overcommit_memory value=1 state=present reload=yes
|
sysctl: name=vm.overcommit_memory value=1 state=present reload=yes
|
||||||
|
|
||||||
|
- name: Template redis.conf file
|
||||||
|
copy:
|
||||||
|
src: /srv/newsblur/docker/redis/redis.conf
|
||||||
|
dest: /srv/newsblur/docker/redis/redis.conf
|
||||||
|
notify: restart redis
|
||||||
|
register: updated_config
|
||||||
|
|
||||||
- name: Template redis_replica.conf file
|
- name: Template redis_replica.conf file
|
||||||
template:
|
template:
|
||||||
src: /srv/newsblur/docker/redis/redis_replica.conf.j2
|
src: /srv/newsblur/docker/redis/redis_replica.conf.j2
|
||||||
|
@ -40,7 +47,7 @@
|
||||||
become: yes
|
become: yes
|
||||||
docker_container:
|
docker_container:
|
||||||
name: redis
|
name: redis
|
||||||
image: redis:6.2.7
|
image: redis:7
|
||||||
state: started
|
state: started
|
||||||
command: /usr/local/etc/redis/redis_server.conf
|
command: /usr/local/etc/redis/redis_server.conf
|
||||||
container_default_behavior: no_defaults
|
container_default_behavior: no_defaults
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"service": {
|
"service": {
|
||||||
{% if inventory_hostname in ["db-redis-user", "db-redis-story1", "db-redis-session", "db-redis-pubsub"] %}
|
{% if inventory_hostname in ["db-redis-user", "db-redis-story2", "db-redis-session", "db-redis-pubsub"] %}
|
||||||
"name": "{{ inventory_hostname|regex_replace('\d+', '') }}",
|
"name": "{{ inventory_hostname|regex_replace('\d+', '') }}",
|
||||||
{% else %}
|
{% else %}
|
||||||
"name": "{{ inventory_hostname|regex_replace('\d+', '') }}-staging",
|
"name": "{{ inventory_hostname|regex_replace('\d+', '') }}-staging",
|
||||||
|
@ -13,15 +13,15 @@
|
||||||
"checks": [{
|
"checks": [{
|
||||||
"id": "{{inventory_hostname}}-ping",
|
"id": "{{inventory_hostname}}-ping",
|
||||||
{% if inventory_hostname.startswith('db-redis-story') %}
|
{% if inventory_hostname.startswith('db-redis-story') %}
|
||||||
"http": "http://{{ ansible_ssh_host }}:5579/db_check/redis_story?consul=1",
|
"http": "http://{{ ansible_host }}:5579/db_check/redis_story?consul=1",
|
||||||
{% elif inventory_hostname.startswith('db-redis-user') %}
|
{% elif inventory_hostname.startswith('db-redis-user') %}
|
||||||
"http": "http://{{ ansible_ssh_host }}:5579/db_check/redis_user?consul=1",
|
"http": "http://{{ ansible_host }}:5579/db_check/redis_user?consul=1",
|
||||||
{% elif inventory_hostname.startswith('db-redis-pubsub') %}
|
{% elif inventory_hostname.startswith('db-redis-pubsub') %}
|
||||||
"http": "http://{{ ansible_ssh_host }}:5579/db_check/redis_pubsub?consul=1",
|
"http": "http://{{ ansible_host }}:5579/db_check/redis_pubsub?consul=1",
|
||||||
{% elif inventory_hostname.startswith('db-redis-sessions') %}
|
{% elif inventory_hostname.startswith('db-redis-sessions') %}
|
||||||
"http": "http://{{ ansible_ssh_host }}:5579/db_check/redis_sessions?consul=1",
|
"http": "http://{{ ansible_host }}:5579/db_check/redis_sessions?consul=1",
|
||||||
{% else %}
|
{% else %}
|
||||||
"http": "http://{{ ansible_ssh_host }}:5000/db_check/redis?consul=1",
|
"http": "http://{{ ansible_host }}:5000/db_check/redis?consul=1",
|
||||||
{% endif %}
|
{% endif %}
|
||||||
"interval": "15s",
|
"interval": "15s",
|
||||||
"failures_before_critical": 4
|
"failures_before_critical": 4
|
||||||
|
|
|
@ -1028,7 +1028,9 @@ class Profile(models.Model):
|
||||||
|
|
||||||
self.setup_premium_history()
|
self.setup_premium_history()
|
||||||
|
|
||||||
if not self.is_premium:
|
if order_id == "nb.premium.archive.99":
|
||||||
|
self.activate_archive()
|
||||||
|
elif not self.is_premium:
|
||||||
self.activate_premium()
|
self.activate_premium()
|
||||||
|
|
||||||
logging.user(self.user, "~FG~BBNew Android premium subscription: $%s~FW" % amount)
|
logging.user(self.user, "~FG~BBNew Android premium subscription: $%s~FW" % amount)
|
||||||
|
|
|
@ -1159,7 +1159,7 @@ def starred_stories_rss_feed_tag(request, user_id, secret_token, tag_slug):
|
||||||
|
|
||||||
def folder_rss_feed(request, user_id, secret_token, unread_filter, folder_slug):
|
def folder_rss_feed(request, user_id, secret_token, unread_filter, folder_slug):
|
||||||
domain = Site.objects.get_current().domain
|
domain = Site.objects.get_current().domain
|
||||||
|
date_hack_2023 = (datetime.datetime.now() > datetime.datetime(2023, 7, 1))
|
||||||
try:
|
try:
|
||||||
user = User.objects.get(pk=user_id)
|
user = User.objects.get(pk=user_id)
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
|
@ -1169,7 +1169,7 @@ def folder_rss_feed(request, user_id, secret_token, unread_filter, folder_slug):
|
||||||
feed_ids, folder_title = user_sub_folders.feed_ids_under_folder_slug(folder_slug)
|
feed_ids, folder_title = user_sub_folders.feed_ids_under_folder_slug(folder_slug)
|
||||||
|
|
||||||
usersubs = UserSubscription.subs_for_feeds(user.pk, feed_ids=feed_ids)
|
usersubs = UserSubscription.subs_for_feeds(user.pk, feed_ids=feed_ids)
|
||||||
if feed_ids and user.profile.is_archive:
|
if feed_ids and ((user.profile.is_archive and date_hack_2023) or (not date_hack_2023)):
|
||||||
params = {
|
params = {
|
||||||
"user_id": user.pk,
|
"user_id": user.pk,
|
||||||
"feed_ids": feed_ids,
|
"feed_ids": feed_ids,
|
||||||
|
@ -1268,7 +1268,7 @@ def folder_rss_feed(request, user_id, secret_token, unread_filter, folder_slug):
|
||||||
rss.add_item(**story_data)
|
rss.add_item(**story_data)
|
||||||
|
|
||||||
# TODO: Remove below date hack to accomodate users who paid for premium but want folder rss
|
# TODO: Remove below date hack to accomodate users who paid for premium but want folder rss
|
||||||
if not user.profile.is_archive and (datetime.datetime.now() > datetime.datetime(2023, 7, 1)):
|
if not user.profile.is_archive and date_hack_2023:
|
||||||
story_data = {
|
story_data = {
|
||||||
'title': "You must have a premium archive subscription on NewsBlur to have RSS feeds for folders.",
|
'title': "You must have a premium archive subscription on NewsBlur to have RSS feeds for folders.",
|
||||||
'link': "https://%s/?next=premium" % domain,
|
'link': "https://%s/?next=premium" % domain,
|
||||||
|
@ -1404,7 +1404,12 @@ def load_river_stories__redis(request):
|
||||||
user_search = None
|
user_search = None
|
||||||
offset = (page-1) * limit
|
offset = (page-1) * limit
|
||||||
story_date_order = "%sstory_date" % ('' if order == 'oldest' else '-')
|
story_date_order = "%sstory_date" % ('' if order == 'oldest' else '-')
|
||||||
|
|
||||||
|
if user.pk == 86178:
|
||||||
|
# Disable Michael_Novakhov account
|
||||||
|
logging.user(request, "~FCLoading ~FMMichael_Novakhov~SN's river, resource usage too high, ignoring.")
|
||||||
|
return HttpResponse("Resource usage too high", status=429)
|
||||||
|
|
||||||
if infrequent:
|
if infrequent:
|
||||||
feed_ids = Feed.low_volume_feeds(feed_ids, stories_per_month=infrequent)
|
feed_ids = Feed.low_volume_feeds(feed_ids, stories_per_month=infrequent)
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ def TaskFeeds():
|
||||||
r.zcard('tasked_feeds'),
|
r.zcard('tasked_feeds'),
|
||||||
r.scard('queued_feeds'),
|
r.scard('queued_feeds'),
|
||||||
r.zcard('scheduled_updates')))
|
r.zcard('scheduled_updates')))
|
||||||
|
logging.debug(" ---> ~FBFeeds being tasked: ~SB%s" % feeds)
|
||||||
|
|
||||||
@app.task(name='task-broken-feeds')
|
@app.task(name='task-broken-feeds')
|
||||||
def TaskBrokenFeeds():
|
def TaskBrokenFeeds():
|
||||||
|
|
|
@ -8,6 +8,7 @@ from django.views.decorators.http import condition
|
||||||
from django.http import HttpResponseForbidden, HttpResponseRedirect, HttpResponse, Http404
|
from django.http import HttpResponseForbidden, HttpResponseRedirect, HttpResponse, Http404
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.contrib.auth.models import User
|
||||||
# from django.db import IntegrityError
|
# from django.db import IntegrityError
|
||||||
from apps.rss_feeds.models import Feed, merge_feeds
|
from apps.rss_feeds.models import Feed, merge_feeds
|
||||||
from apps.rss_feeds.models import MFetchHistory
|
from apps.rss_feeds.models import MFetchHistory
|
||||||
|
@ -510,19 +511,21 @@ def status(request):
|
||||||
return HttpResponseForbidden()
|
return HttpResponseForbidden()
|
||||||
minutes = int(request.GET.get('minutes', 1))
|
minutes = int(request.GET.get('minutes', 1))
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
|
hour_ago = now + datetime.timedelta(minutes=minutes)
|
||||||
username = request.GET.get('user', '') or request.GET.get('username', '')
|
username = request.GET.get('user', '') or request.GET.get('username', '')
|
||||||
if username:
|
if username == "all":
|
||||||
user = User.objects.get(username=username)
|
feeds = Feed.objects.filter(next_scheduled_update__lte=hour_ago).order_by('next_scheduled_update')
|
||||||
else:
|
else:
|
||||||
user = request.user
|
if username:
|
||||||
usersubs = UserSubscription.objects.filter(user=user)
|
user = User.objects.get(username=username)
|
||||||
feed_ids = usersubs.values('feed_id')
|
else:
|
||||||
if minutes > 0:
|
user = request.user
|
||||||
hour_ago = now + datetime.timedelta(minutes=minutes)
|
usersubs = UserSubscription.objects.filter(user=user)
|
||||||
feeds = Feed.objects.filter(pk__in=feed_ids, next_scheduled_update__lte=hour_ago).order_by('next_scheduled_update')
|
feed_ids = usersubs.values('feed_id')
|
||||||
else:
|
if minutes > 0:
|
||||||
hour_ago = now + datetime.timedelta(minutes=minutes)
|
feeds = Feed.objects.filter(pk__in=feed_ids, next_scheduled_update__lte=hour_ago).order_by('next_scheduled_update')
|
||||||
feeds = Feed.objects.filter(pk__in=feed_ids, last_update__gte=hour_ago).order_by('-last_update')
|
else:
|
||||||
|
feeds = Feed.objects.filter(pk__in=feed_ids, last_update__gte=hour_ago).order_by('-last_update')
|
||||||
|
|
||||||
r = redis.Redis(connection_pool=settings.REDIS_FEED_UPDATE_POOL)
|
r = redis.Redis(connection_pool=settings.REDIS_FEED_UPDATE_POOL)
|
||||||
queues = {
|
queues = {
|
||||||
|
|
|
@ -5,4 +5,5 @@ urlpatterns = [
|
||||||
url(r'^dashboard_graphs', views.dashboard_graphs, name='statistics-graphs'),
|
url(r'^dashboard_graphs', views.dashboard_graphs, name='statistics-graphs'),
|
||||||
url(r'^feedback_table', views.feedback_table, name='feedback-table'),
|
url(r'^feedback_table', views.feedback_table, name='feedback-table'),
|
||||||
url(r'^revenue', views.revenue, name='revenue'),
|
url(r'^revenue', views.revenue, name='revenue'),
|
||||||
|
url(r'^slow', views.slow, name='slow'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
|
import base64
|
||||||
|
import pickle
|
||||||
|
import redis
|
||||||
import datetime
|
import datetime
|
||||||
|
from operator import countOf
|
||||||
|
from collections import defaultdict
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.contrib.auth.models import AnonymousUser
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.conf import settings
|
||||||
from django.utils import feedgenerator
|
from django.utils import feedgenerator
|
||||||
|
from django.http import HttpResponseForbidden
|
||||||
from apps.statistics.models import MStatistics, MFeedback
|
from apps.statistics.models import MStatistics, MFeedback
|
||||||
|
from apps.statistics.rstats import round_time
|
||||||
from apps.profile.models import PaymentHistory
|
from apps.profile.models import PaymentHistory
|
||||||
from utils import log as logging
|
from utils import log as logging
|
||||||
|
|
||||||
|
|
||||||
def dashboard_graphs(request):
|
def dashboard_graphs(request):
|
||||||
statistics = MStatistics.all()
|
statistics = MStatistics.all()
|
||||||
return render(
|
return render(
|
||||||
|
@ -49,4 +59,60 @@ def revenue(request):
|
||||||
request.META.get('HTTP_USER_AGENT', "")[:24]
|
request.META.get('HTTP_USER_AGENT', "")[:24]
|
||||||
))
|
))
|
||||||
return HttpResponse(rss.writeString('utf-8'), content_type='application/rss+xml')
|
return HttpResponse(rss.writeString('utf-8'), content_type='application/rss+xml')
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def slow(request):
|
||||||
|
r = redis.Redis(connection_pool=settings.REDIS_STATISTICS_POOL)
|
||||||
|
if not request.user.is_staff and not settings.DEBUG:
|
||||||
|
logging.user(request, "~SKNON-STAFF VIEWING SLOW STATUS!")
|
||||||
|
assert False
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
all_queries = {}
|
||||||
|
user_id_counts = {}
|
||||||
|
path_counts = {}
|
||||||
|
users = {}
|
||||||
|
|
||||||
|
for minutes_ago in range(60*6):
|
||||||
|
dt_ago = now - datetime.timedelta(minutes=minutes_ago)
|
||||||
|
minute = round_time(dt_ago, round_to=60)
|
||||||
|
dt_ago_str = minute.strftime("%a %b %-d, %Y %H:%M")
|
||||||
|
name = f"SLOW:{minute.strftime('%s')}"
|
||||||
|
minute_queries = r.lrange(name, 0, -1)
|
||||||
|
for query_raw in minute_queries:
|
||||||
|
query = pickle.loads(base64.b64decode(query_raw))
|
||||||
|
user_id = query['user_id']
|
||||||
|
if dt_ago_str not in all_queries:
|
||||||
|
all_queries[dt_ago_str] = []
|
||||||
|
if user_id in users:
|
||||||
|
user = users[user_id]
|
||||||
|
elif int(user_id) != 0:
|
||||||
|
try:
|
||||||
|
user = User.objects.get(pk=user_id)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
continue
|
||||||
|
users[user_id] = user
|
||||||
|
else:
|
||||||
|
user = AnonymousUser()
|
||||||
|
users[user_id] = user
|
||||||
|
query['user'] = user
|
||||||
|
query['datetime'] = minute
|
||||||
|
all_queries[dt_ago_str].append(query)
|
||||||
|
if user_id not in user_id_counts:
|
||||||
|
user_id_counts[user_id] = 0
|
||||||
|
user_id_counts[user_id] += 1
|
||||||
|
if query['path'] not in path_counts:
|
||||||
|
path_counts[query['path']] = 0
|
||||||
|
path_counts[query['path']] += 1
|
||||||
|
|
||||||
|
user_counts = []
|
||||||
|
for user_id, count in user_id_counts.items():
|
||||||
|
user_counts.append({'user': users[user_id], 'count': count})
|
||||||
|
|
||||||
|
return render(request, 'statistics/slow.xhtml', {
|
||||||
|
'all_queries': all_queries,
|
||||||
|
'user_counts': user_counts,
|
||||||
|
'path_counts': path_counts,
|
||||||
|
})
|
||||||
|
|
|
@ -54,8 +54,8 @@ android {
|
||||||
applicationId "com.newsblur"
|
applicationId "com.newsblur"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 31
|
targetSdkVersion 31
|
||||||
versionCode 205
|
versionCode 207
|
||||||
versionName "12.0.1"
|
versionName "12.1.1"
|
||||||
}
|
}
|
||||||
compileOptions.with {
|
compileOptions.with {
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
|
Binary file not shown.
|
@ -63,7 +63,7 @@ frontend public
|
||||||
|
|
||||||
use_backend app_push if { hdr_end(host) -i push.newsblur.com }
|
use_backend app_push if { hdr_end(host) -i push.newsblur.com }
|
||||||
use_backend node_socket if { path_beg /v3/socket.io/ }
|
use_backend node_socket if { path_beg /v3/socket.io/ }
|
||||||
use_backend node_favicon if { path_beg /rss_feeds/icon/ }
|
use_backend node_favicons if { path_beg /rss_feeds/icon/ }
|
||||||
use_backend node_text if { path_beg /rss_feeds/original_text_fetcher }
|
use_backend node_text if { path_beg /rss_feeds/original_text_fetcher }
|
||||||
use_backend node_images if { hdr_end(host) -i imageproxy.newsblur.com }
|
use_backend node_images if { hdr_end(host) -i imageproxy.newsblur.com }
|
||||||
use_backend node_images if { hdr_end(host) -i imageproxy2.newsblur.com }
|
use_backend node_images if { hdr_end(host) -i imageproxy2.newsblur.com }
|
||||||
|
@ -174,7 +174,7 @@ backend node_socket
|
||||||
server {{host}} {{host}}.node.nyc1.consul:8008
|
server {{host}} {{host}}.node.nyc1.consul:8008
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
backend node_favicon
|
backend node_favicons
|
||||||
http-check expect rstatus 200|503
|
http-check expect rstatus 200|503
|
||||||
option httpchk GET /rss_feeds/icon/1
|
option httpchk GET /rss_feeds/icon/1
|
||||||
balance roundrobin
|
balance roundrobin
|
||||||
|
@ -184,7 +184,10 @@ backend node_text
|
||||||
http-check expect rstatus 200|503
|
http-check expect rstatus 200|503
|
||||||
option httpchk GET /rss_feeds/original_text_fetcher?test=1
|
option httpchk GET /rss_feeds/original_text_fetcher?test=1
|
||||||
balance roundrobin
|
balance roundrobin
|
||||||
server-template node-text 1 _node-text._tcp.service.nyc1.consul:8008 check inter 2000ms resolvers consul resolve-prefer ipv4 resolve-opts allow-dup-ip init-addr none
|
default-server check inter 2000ms resolvers consul resolve-prefer ipv4 resolve-opts allow-dup-ip init-addr none
|
||||||
|
{% for host in groups.node_text %}
|
||||||
|
server {{host}} {{host}}.node.nyc1.consul:8008
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
backend node_page
|
backend node_page
|
||||||
http-check expect rstatus 200|503
|
http-check expect rstatus 200|503
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -19,6 +19,6 @@
|
||||||
.NB-status td {
|
.NB-status td {
|
||||||
border-top: 1px solid #F0F0F0;
|
border-top: 1px solid #F0F0F0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0 0;
|
padding: 0 6px 0 0;
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ AUTO_PREMIUM_NEW_USERS = True
|
||||||
AUTO_PREMIUM_ARCHIVE_NEW_USERS = True
|
AUTO_PREMIUM_ARCHIVE_NEW_USERS = True
|
||||||
AUTO_PREMIUM_PRO_NEW_USERS = True
|
AUTO_PREMIUM_PRO_NEW_USERS = True
|
||||||
AUTO_PREMIUM = True
|
AUTO_PREMIUM = True
|
||||||
AUTO_PREMIUM = False
|
# AUTO_PREMIUM = False
|
||||||
if not AUTO_PREMIUM:
|
if not AUTO_PREMIUM:
|
||||||
AUTO_PREMIUM_NEW_USERS = False
|
AUTO_PREMIUM_NEW_USERS = False
|
||||||
AUTO_PREMIUM_ARCHIVE_NEW_USERS = False
|
AUTO_PREMIUM_ARCHIVE_NEW_USERS = False
|
||||||
|
|
|
@ -71,6 +71,6 @@ favicons = (app) =>
|
||||||
if ENV_DEV or ENV_DOCKER
|
if ENV_DEV or ENV_DOCKER
|
||||||
res.redirect '/media/img/icons/nouns/world.svg'
|
res.redirect '/media/img/icons/nouns/world.svg'
|
||||||
else
|
else
|
||||||
res.redirect 'https://beta.newsblur.com/media/img/icons/nouns/world.svg'
|
res.redirect 'https://newsblur.com/media/img/icons/nouns/world.svg'
|
||||||
|
|
||||||
exports.favicons = favicons
|
exports.favicons = favicons
|
||||||
|
|
|
@ -88,7 +88,7 @@
|
||||||
if (ENV_DEV || ENV_DOCKER) {
|
if (ENV_DEV || ENV_DOCKER) {
|
||||||
return res.redirect('/media/img/icons/nouns/world.svg');
|
return res.redirect('/media/img/icons/nouns/world.svg');
|
||||||
} else {
|
} else {
|
||||||
return res.redirect('https://beta.newsblur.com/media/img/icons/nouns/world.svg');
|
return res.redirect('https://newsblur.com/media/img/icons/nouns/world.svg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -85,7 +85,7 @@
|
||||||
<img src="/media/img/logo_512.png" class="logo">
|
<img src="/media/img/logo_512.png" class="logo">
|
||||||
<h1>NewsBlur is in <span class="error404">maintenance mode</span></h1>
|
<h1>NewsBlur is in <span class="error404">maintenance mode</span></h1>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<p>Moving to a larger Redis story DB, since the existing DB is buckling under the new load from the requirements of the new NewsBlur Premium Archive subscription.</p>
|
<p>Moving to another Redis story DB, since the existing DB is having some issues. Check the daily load time graph to see how it's had an impact. Always good to do this, expect fast load times after this.</p>
|
||||||
<p>To pass the time, <a href="http://mltshp.com/popular">check out what's popular on MLTSHP</a>.</p>
|
<p>To pass the time, <a href="http://mltshp.com/popular">check out what's popular on MLTSHP</a>.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
{% load utils_tags tz %}
|
{% load utils_tags tz %}
|
||||||
|
|
||||||
{% block bodyclass %}NB-body-status{% endblock %}
|
{% block bodyclass %}NB-body-status NB-static{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@
|
||||||
<th style="white-space: nowrap">Last Update<br>Next Update</th>
|
<th style="white-space: nowrap">Last Update<br>Next Update</th>
|
||||||
<th>Min to<br>next update</th>
|
<th>Min to<br>next update</th>
|
||||||
<th>Decay</th>
|
<th>Decay</th>
|
||||||
|
<th>Last fetch</th>
|
||||||
<th>Subs</th>
|
<th>Subs</th>
|
||||||
<th>Active</th>
|
<th>Active</th>
|
||||||
<th>Premium</th>
|
<th>Premium</th>
|
||||||
|
@ -29,11 +30,13 @@
|
||||||
<th>Act. Prem</th>
|
<th>Act. Prem</th>
|
||||||
<th>Per Month</th>
|
<th>Per Month</th>
|
||||||
<th>Last Month</th>
|
<th>Last Month</th>
|
||||||
|
<th>In Archive</th>
|
||||||
|
<th>File size (b)</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for feed in feeds %}
|
{% for feed in feeds %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ feed.pk }}</td>
|
<td>{{ feed.pk }}</td>
|
||||||
<td><img class="NB-favicon" src="/rss_feeds/icon/{{ feed.pk }}" /> {{ feed.feed_title|truncatewords:4 }}</td>
|
<td title="{{ feed.feed_address }}"><img class="NB-favicon" src="/rss_feeds/icon/{{ feed.pk }}" /> {{ feed.feed_title|truncatewords:4 }}</td>
|
||||||
<td>{{ feed.last_update|smooth_timedelta }}</td>
|
<td>{{ feed.last_update|smooth_timedelta }}</td>
|
||||||
<td class="NB-status-update" style="white-space: nowrap">
|
<td class="NB-status-update" style="white-space: nowrap">
|
||||||
{% localdatetime feed.last_update "%b %d, %Y %H:%M:%S" %}
|
{% localdatetime feed.last_update "%b %d, %Y %H:%M:%S" %}
|
||||||
|
@ -42,6 +45,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td>{{ feed.next_scheduled_update|smooth_timedelta }}</td>
|
<td>{{ feed.next_scheduled_update|smooth_timedelta }}</td>
|
||||||
<td>{{ feed.min_to_decay }}</td>
|
<td>{{ feed.min_to_decay }}</td>
|
||||||
|
<td>{{ feed.last_load_time }}</td>
|
||||||
<td>{{ feed.num_subscribers }}</td>
|
<td>{{ feed.num_subscribers }}</td>
|
||||||
<td style="color: {% if feed.active_subscribers == 0 %}lightgrey{% else %}darkblue{% endif %}">{{ feed.active_subscribers }}</td>
|
<td style="color: {% if feed.active_subscribers == 0 %}lightgrey{% else %}darkblue{% endif %}">{{ feed.active_subscribers }}</td>
|
||||||
<td style="color: {% if feed.premium_subscribers == 0 %}lightgrey{% else %}darkblue{% endif %}">{{ feed.premium_subscribers }}</td>
|
<td style="color: {% if feed.premium_subscribers == 0 %}lightgrey{% else %}darkblue{% endif %}">{{ feed.premium_subscribers }}</td>
|
||||||
|
@ -50,6 +54,8 @@
|
||||||
<td style="color: {% if feed.active_premium_subscribers == 0 %}lightgrey{% else %}darkblue{% endif %}">{{ feed.active_premium_subscribers }}</td>
|
<td style="color: {% if feed.active_premium_subscribers == 0 %}lightgrey{% else %}darkblue{% endif %}">{{ feed.active_premium_subscribers }}</td>
|
||||||
<td style="color: {% if feed.average_stories_per_month == 0 %}lightgrey{% else %}{% endif %}">{{ feed.average_stories_per_month }}</td>
|
<td style="color: {% if feed.average_stories_per_month == 0 %}lightgrey{% else %}{% endif %}">{{ feed.average_stories_per_month }}</td>
|
||||||
<td style="color: {% if feed.stories_last_month == 0 %}lightgrey{% else %}{% endif %}">{{ feed.stories_last_month }}</td>
|
<td style="color: {% if feed.stories_last_month == 0 %}lightgrey{% else %}{% endif %}">{{ feed.stories_last_month }}</td>
|
||||||
|
<td style="color: {% if feed.archive_count == 0 %}lightgrey{% else %}{% endif %}">{{ feed.archive_count }}</td>
|
||||||
|
<td style="color: {% if feed.fs_size_bytes == 0 %}lightgrey{% else %}{% endif %}">{{ feed.fs_size_bytes|commify }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|
54
templates/statistics/slow.xhtml
Normal file
54
templates/statistics/slow.xhtml
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% load utils_tags tz %}
|
||||||
|
|
||||||
|
{% block bodyclass %}NB-body-status NB-static{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="NB-module">
|
||||||
|
|
||||||
|
<div class="queries">
|
||||||
|
<table class="NB-status">
|
||||||
|
{% for user_count in user_counts %}
|
||||||
|
<tr>
|
||||||
|
{% if forloop.first %}<td rowspan={{user_counts|length}} valign=top><b>Users</b>{% endif %}
|
||||||
|
<td><b>{{ user_count.user }}</b></td>
|
||||||
|
<td>{{ user_count.count }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="queries">
|
||||||
|
<table class="NB-status">
|
||||||
|
{% for path, count in path_counts.items %}
|
||||||
|
<tr>
|
||||||
|
{% if forloop.first %}<td rowspan={{path_counts|length}} valign=top><b>Paths</b>{% endif %}
|
||||||
|
<td><b>{{ path }}</b></td>
|
||||||
|
<td>{{ count }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="NB-status">
|
||||||
|
{% for dt_str, queries in all_queries.items %}
|
||||||
|
{% for query in queries %}
|
||||||
|
<tr>
|
||||||
|
{% if forloop.first %}
|
||||||
|
<td rowspan={{ queries|length }} valign=top> <b>
|
||||||
|
{% localdatetime query.datetime "%a %b %d, %Y %H:%M" %}
|
||||||
|
</b></td>
|
||||||
|
{% endif %}
|
||||||
|
<td>{{ query.user }}</td>
|
||||||
|
<td>{{ query.time }}</td>
|
||||||
|
<td>{{ query.method }}</td>
|
||||||
|
<td>{{ query.path }}</td>
|
||||||
|
<td>{% if query.data %}{{ query.data }}{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endblock content %}
|
|
@ -228,8 +228,9 @@ resource "digitalocean_droplet" "discovery" {
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "digitalocean_droplet" "node-text" {
|
resource "digitalocean_droplet" "node-text" {
|
||||||
|
count = 2
|
||||||
image = var.droplet_os
|
image = var.droplet_os
|
||||||
name = "node-text"
|
name = contains([0], count.index) ? "node-text" : "node-text${count.index+1}"
|
||||||
region = var.droplet_region
|
region = var.droplet_region
|
||||||
size = var.droplet_size
|
size = var.droplet_size
|
||||||
ssh_keys = [digitalocean_ssh_key.default.fingerprint]
|
ssh_keys = [digitalocean_ssh_key.default.fingerprint]
|
||||||
|
@ -263,8 +264,9 @@ resource "digitalocean_droplet" "node-socket" {
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "digitalocean_droplet" "node-favicons" {
|
resource "digitalocean_droplet" "node-favicons" {
|
||||||
|
count = 2
|
||||||
image = var.droplet_os
|
image = var.droplet_os
|
||||||
name = "node-favicons"
|
name = "node-favicons${count.index+1}"
|
||||||
region = var.droplet_region
|
region = var.droplet_region
|
||||||
size = var.droplet_size
|
size = var.droplet_size
|
||||||
ssh_keys = [digitalocean_ssh_key.default.fingerprint]
|
ssh_keys = [digitalocean_ssh_key.default.fingerprint]
|
||||||
|
@ -378,11 +380,12 @@ resource "digitalocean_droplet" "db-redis-sessions" {
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "digitalocean_droplet" "db-redis-story" {
|
resource "digitalocean_droplet" "db-redis-story" {
|
||||||
count = 1
|
count = 2
|
||||||
image = var.droplet_os
|
image = var.droplet_os
|
||||||
name = "db-redis-story${count.index+1}"
|
name = "db-redis-story${count.index+1}"
|
||||||
region = var.droplet_region
|
region = var.droplet_region
|
||||||
size = contains([1], count.index) ? "m-8vcpu-64gb" : var.redis_story_droplet_size
|
size = contains([1], count.index) ? "m-8vcpu-64gb" : var.redis_story_droplet_size
|
||||||
|
# size = var.redis_story_droplet_size
|
||||||
ssh_keys = [digitalocean_ssh_key.default.fingerprint]
|
ssh_keys = [digitalocean_ssh_key.default.fingerprint]
|
||||||
provisioner "local-exec" {
|
provisioner "local-exec" {
|
||||||
command = "/srv/newsblur/ansible/utils/generate_inventory.py; sleep 120"
|
command = "/srv/newsblur/ansible/utils/generate_inventory.py; sleep 120"
|
||||||
|
@ -464,12 +467,13 @@ resource "digitalocean_droplet" "db-postgres" {
|
||||||
# servers=$(for i in {1..9}; do echo -n "-target=\"digitalocean_droplet.db-mongo-primary[$i]\" " ; done); tf plan -refresh=false `eval echo $servers`
|
# servers=$(for i in {1..9}; do echo -n "-target=\"digitalocean_droplet.db-mongo-primary[$i]\" " ; done); tf plan -refresh=false `eval echo $servers`
|
||||||
#
|
#
|
||||||
resource "digitalocean_droplet" "db-mongo-primary" {
|
resource "digitalocean_droplet" "db-mongo-primary" {
|
||||||
count = 1
|
count = 2
|
||||||
backups = contains([0], count.index) ? false : true
|
backups = contains([0], count.index) ? false : true
|
||||||
image = var.droplet_os
|
image = var.droplet_os
|
||||||
name = "db-mongo-primary${count.index+1}"
|
name = "db-mongo-primary${count.index+1}"
|
||||||
region = var.droplet_region
|
region = var.droplet_region
|
||||||
size = contains([1], count.index) ? "m3-8vcpu-64gb" : var.mongo_primary_droplet_size
|
# size = contains([1], count.index) ? "m3-8vcpu-64gb" : var.mongo_primary_droplet_size
|
||||||
|
size = var.mongo_primary_droplet_size
|
||||||
ssh_keys = [digitalocean_ssh_key.default.fingerprint]
|
ssh_keys = [digitalocean_ssh_key.default.fingerprint]
|
||||||
provisioner "local-exec" {
|
provisioner "local-exec" {
|
||||||
command = "/srv/newsblur/ansible/utils/generate_inventory.py; sleep 120"
|
command = "/srv/newsblur/ansible/utils/generate_inventory.py; sleep 120"
|
||||||
|
|
|
@ -54,7 +54,7 @@ variable "droplet_os" {
|
||||||
|
|
||||||
variable "sentry_droplet_size" {
|
variable "sentry_droplet_size" {
|
||||||
type = string
|
type = string
|
||||||
default = "s-4vcpu-8gb"
|
default = "s-8vcpu-16gb"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "metrics_droplet_size" {
|
variable "metrics_droplet_size" {
|
||||||
|
@ -89,5 +89,5 @@ variable "elasticsearch_droplet_size" {
|
||||||
|
|
||||||
variable "redis_story_droplet_size" {
|
variable "redis_story_droplet_size" {
|
||||||
type = string
|
type = string
|
||||||
default = "m-8vcpu-64gb"
|
default = "m-4vcpu-32gb"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from utils import log as logging
|
from utils import log as logging
|
||||||
|
from apps.statistics.rstats import round_time
|
||||||
|
import pickle
|
||||||
|
import base64
|
||||||
import time
|
import time
|
||||||
|
import redis
|
||||||
|
|
||||||
IGNORE_PATHS = [
|
IGNORE_PATHS = [
|
||||||
"/_haproxychk",
|
"/_haproxychk",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
RECORD_SLOW_REQUESTS_ABOVE_SECONDS = 10
|
||||||
|
|
||||||
class DumpRequestMiddleware:
|
class DumpRequestMiddleware:
|
||||||
def process_request(self, request):
|
def process_request(self, request):
|
||||||
if settings.DEBUG and request.path not in IGNORE_PATHS:
|
if settings.DEBUG and request.path not in IGNORE_PATHS:
|
||||||
|
@ -40,22 +46,31 @@ class DumpRequestMiddleware:
|
||||||
redis_log
|
redis_log
|
||||||
))
|
))
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
def elapsed_time(self, request):
|
|
||||||
time_elapsed = ""
|
|
||||||
if hasattr(request, 'start_time'):
|
if hasattr(request, 'start_time'):
|
||||||
seconds = time.time() - request.start_time
|
seconds = time.time() - request.start_time
|
||||||
color = '~FB'
|
if seconds > RECORD_SLOW_REQUESTS_ABOVE_SECONDS:
|
||||||
if seconds >= 1:
|
r = redis.Redis(connection_pool=settings.REDIS_STATISTICS_POOL)
|
||||||
color = '~FR'
|
pipe = r.pipeline()
|
||||||
elif seconds > .2:
|
minute = round_time(round_to=60)
|
||||||
color = '~SB~FK'
|
name = f"SLOW:{minute.strftime('%s')}"
|
||||||
time_elapsed = "[%s%.4ss~SB] " % (
|
user_id = request.user.pk if request.user.is_authenticated else "0"
|
||||||
color,
|
data_string = None
|
||||||
seconds,
|
if request.method == "GET":
|
||||||
)
|
data_string = ' '.join([f"{key}={value}" for key, value in request.GET.items()])
|
||||||
return time_elapsed
|
elif request.method == "GET":
|
||||||
|
data_string = ' '.join([f"{key}={value}" for key, value in request.POST.items()])
|
||||||
|
data = {
|
||||||
|
"user_id": user_id,
|
||||||
|
"time": round(seconds, 2),
|
||||||
|
"path": request.path,
|
||||||
|
"method": request.method,
|
||||||
|
"data": data_string,
|
||||||
|
}
|
||||||
|
pipe.lpush(name, base64.b64encode(pickle.dumps(data)).decode('utf-8'))
|
||||||
|
pipe.expire(name, 60*60*12) # 12 hours
|
||||||
|
pipe.execute()
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
def color_db(self, seconds, default):
|
def color_db(self, seconds, default):
|
||||||
color = default
|
color = default
|
||||||
|
|
Loading…
Add table
Reference in a new issue