Tag Archives: Sentry

django database router是个好东西

今天给sentry加了MySQL读写分离机制,记录一下: DATABASES里,(sentry的脾气比较怪)保留名为default的配置,写master数据库的参数;新增一个名为slave的配置,使用只读用户名密码,或开启服务器端read only 然后增加一个类,带四个函数: class DatabaseRWSplitRouter(object): def db_for_read(self, model, **hints): return ‘slave’ def db_for_write(self, model, **hints): return ‘default’ def allow_relation(self, model1, model2, **hints): return True def allow_migrate(self, db, app_label, model_name=None, **hints): if db == ‘slave’: return None else: return True … Continue reading

Posted in 默认分类 | Tagged , , | Leave a comment

Sentry新版SSO Provider讲解

从GH-1372 issue完成时开始,Sentry 7.x转向使用自家定义的SSO Provider,逐渐抛弃django-social-auth结构。因为缺乏文档,我在此事上消耗了大量的时间。现在写这篇Wiki用于记录: 基类:sentry/auth/provider.py 中的 Provider 虚基类 样例:sentry/auth/providers/ 目录下的dummy.py和oauth2.py 两个,以及sentry-sso-google 基于Sentry自带的OAuth2Provider   基本执行流程: 通过django机制加载app,在初始化时调用sentry.auth.manager的register方法,注册自己的名字和class 管理员在 Organization 的Auth页开启SSO时,会生成上述类的一个实例。Sentry的helper.py负责此流程,先调用get_auth_pipeline()函数,取得一个成员为多个 AuthView的超类 的list,然后对着这个list依次执行成员的 dispatch()函数 每个步骤按照pipeline的顺序执行一个AuthView.dispatch(self,request,helper)函数,其中request可以读到当前的django request内容。因为URL都为/auth/sso/ 所以需要根据querystring来判断执行到哪一步了,并收集本步的信息,调用 helper.bind_state() 保存起来,然后return help.next_step()跳到下一步执行 最后build_identify()会被执行,需要返回一个至少包含id、email、name的dict,此dict被Sentry用来构造用户信息。

Posted in 默认分类 | Tagged , | Leave a comment

sentry-公司内IM插件

这插件被大家催了很久了,到1月底终于决定动手做。 准备知识 公众号Pub服务API(内网wiki,链接已删除) 签名请求规范(内网wiki,链接已删除) New Forms of Authentication 插件组成 setup.py 安装脚本。插件注册发现机制请参见 sentry_IM/__init__.py 确保这个目录是python package的文件。顺便查询一下VERSION供引用 sentry_IM/requests_APISign.py 上述New Forms of Authentication文档里提到的requests auth class,提供公司的Authencation验证header sentry_IM/send_IM.py 发消息用的Class sentry_IM/plugin.py 插件本身 程序讲解 插件本身继承自sentry.plugins.bases.notify.NotificationPlugin类,需要实现其notify(self, notification)方法。因基类里notify()调用了notify_users(),所以pylint会有提示。 参数notification为一个对象,可以从中取得event,然后从event取得所属的group以及group所属的project。根据project可以取得要通知的人员列表。(这里抄袭了MailPlugin的发送列表)因为这个函数得到的人员列表是Sentry里的User id,所以还需要再根据id查询到User,然后从其Email字段中取得localpart 最后,根据上述project.name、event.message、group.get_absolute_url() 组装其通知消息文本(URL在IM客户端渲染时才变成超链接,我这里只发URL本身),生成send_IM.py 的实例,对着上述人员列表发送消息文本。 IM插件所需的四个参数,在总的配置文件中列出,由插件生成send_IM实例时传递,然后由其传递给requests_APISign的实例,完成对HTTP请求的签名 配置是否开启 本来是想像MailPlugin那样直接在Account/Notification页做个列表供勾选。但阅读代码发现,这个功能并不是MailPlugin自己的功能,而是sentry做了特殊处理,先获得用户订阅的projects_list,然后循环生成ProjectEmailOptionsForm的list,随后再执行从各插件获取的ext_forms[]配置表单。ext_forms里每一个插件配置表单的初始化参数只有plugin、user和prefix,要想表达“所有可订阅的projects”这个概念会相当啰嗦。因此,最终确定为:表单里只勾选per-User 级别是否开启;而用户是否属于具体某个event/project的发送目标,则直接共用MailPlugin那边的选择,然后再用上述per-User级别开关做一遍过滤。这里两个插件之间略有粘连,主要是由于User-Project之间只有单向对应关系,逆向查询太罗嗦导致的。 最终发送效果 Github地址 https://github.com/julyclyde/sentry-IM

Posted in 默认分类 | Tagged , | Leave a comment

Sentry 新版插件基类讲解

根据 sentry/plugins/bases/__init__.py 的说明,新版插件分类四类,其中常见的: NotificationPlugin 比如sentry_mail、sentry_webhook插件 NotificationPlugin的基类定义参见 sentry/plugins/bases/notify.py

Posted in 默认分类 | Tagged , | Leave a comment

Sentry整理杂记

本讨论均基于Sentry 7.7版本 插件机制 自带插件 src/sentry/plugins/ 每插件一个目录 自带插件loader:src/sentry/conf/server.py 里的INSTALLED_APPS tuple 外装插件loader:utils/runner.py 里的 install_plugins()函数,对iter_entry_points()遍历并将其加入INSTALLED_APPS tuple中 外装插件注册:插件的setup.py里执行注册entry_points的过程。例https://github.com/getsentry/sentry-groveio/blob/master/setup.py 参考setuptools:pkg_resources 参考资料:https://pythonhosted.org/setuptools/pkg_resources.html 列出本Python安装的Sentry插件 #!/usr/bin/env python import pkg_resources for ep in pkg_resources.iter_entry_points(‘sentry.apps’):     print str(ep) for ep in pkg_resources.iter_entry_points(‘sentry.plugins’):     print str(ep)   sentry-jira插件 插件基本配置 每Project分别配置,需要输入jira的instance URL、用户名和密码。以上内容base64存在数据库sentry_projectoptions表里 where `key` … Continue reading

Posted in 默认分类 | Tagged , | 1 Comment

SSO跳回sentry失败的解决方法

从某月开始,公司SSO回传信息改用POST方式,放弃了之前的通过querystring传递的做法。具体到Sentry这里,因为 /auth/sso 页面受CSRF保护,拒绝接受POST回来的不含CSRF token的数据,从而无法登录。 解决这个问题,可能有三种方法吧: 让SSO回传csrf token:前提是得先把csrf token发给SSO,且SSO方愿意配合修改。考虑到现在跳转到SSO去是直接302的,CSRF如果放在querystring上,其实和他们回传时把token放在querystring上风险相当了,所以这做法不行 Sentry/auth/sso不验证CSRF:/auth/sso 对应AuthProviderLoginView,该View继承自BaseView,而BaseView的dispatch()方法是@method_decorator(csrf_protect)过的,因为还有其它很多View都这么继承,想改基类还怕有别的风险。想在配置文件里monkey patch掉AuthProviderLoginView,但是import sentry.auth.helper的时候就失败了 Sentry整体不验证CSRF:试了试,csrf view middleware可以去掉,但是,csrf_protect decorator依然生效。关于此处,django文档说的不太精确。该middleware除了负责种cookie,还负责验证,但实际上验证工作并不是以middleware的身份来做的,而是以decorator的身份来做的。单单禁止middleware的加载只是去掉了种cookie的行为而已。网上也有其他人遭遇过这个问题 http://www.douban.com/group/topic/11555679/ 最后,参照3里帖子的做法,做了一个django middleware插在csrf view middleware前面,如当前请求URI为/auth/sso/ 就取消CSRF保护。

Posted in 默认分类 | Tagged , | Leave a comment

记一次Sentry的性能调优过程

大概从6月底开始,我们的Sentry(错误日志收集、聚合和报警系统 http://getsentry.com )遭遇了性能问题,每分钟只能处理200个事件了,经常有20多万待处理的任务积压在events队列里,延迟超过一小时,我不得不丢弃这些任务,清空队列,以促进时效。重启一下celery worker会有瞬间的改善,但很快就又不行了,似乎worker的性能会衰减。为此,我给celery worker增加了–time-limit参数,使worker不会在执行不下去的时候无限等待,而是超时出错退出,迎接新的任务,情况略有改善。 因为Sentry的结构是前端web-消息队列-后端celery这样的结构,给调试也造成了一些困难。 我先开启了celery worker的DEBUG级别日志,从中搜索包含succeed的行,然后把其中UUID给过滤掉,只保留任务名字和耗时,整理后得到任务和耗时的对应关系,发现save_event这个任务的耗时很有意思,经常是6秒的整倍数。 然后去这个任务里添加时间打点代码,发现在它调用的EventManager.save()函数里,带事务执行的三次数据库插入(调用_save_aggregate、创建EventMapping、保存Event)的耗时,不是零点几秒,就是六秒的整倍数,而完全没有2、3秒、7、8秒这种情况。于是检查的方向就被引导到了数据库方面。折腾了DBA同事几天,删了几个索引和外键约束,也没什么改善。 想到之前为了Sentry的下一个版本v8准备过一套PostgreSQL数据库用于测试,我就拿过来,另外建了一套Sentry 7的部署,在上面运行,没想到性能极其丝滑……事情似乎越来越明了,就是数据库的问题。不过当我停止使用用于测试的128G内存真机PostgreSQL,而自己申请了一个和当前MySQL一样配置的16G机器的时候,发现低配置时pgsql的性能好像还不如MySQL……不过就在我从真实环境往测试环境export/import数据的时候,发现了Sentry在执行import时,如果测试环境共用生产系统的Redis,就会速度极慢,几乎卡死,用strace上去看一下发现慢在Redis操作上,开销在数据库上的时间极少;而如果用专用的一套Redis就速度极快。 于是我的注意力又转向了Redis。尝试FLUSHDB,发现性能立刻恢复了每分钟4000条、峰值8000条的处理能力,但redis的内存碎片率上涨了不少。看了看SLOWLOG,发现有一条几乎占满了整个SLOWLOG排行榜的命令 ZRANGE b:p 0 -1 手工试了试,这条命令得好几秒种才能返回,一次就能返回数据好几万条。搜了一下源代码,在sentry/buffer/redis.py文件发现了b:p这个key名字。原来,这是把高速更新的计数器的多次更新合并起来,减轻数据库压力的一个组件。但这个组件本身因为把sorted set用到了极限,所以性能不佳。考虑到不能再让碎片率上涨,我只好偶尔手工删除这个key,放弃一些计数器更新操作:放弃更新计数器总比放弃整个任务看起来要好些吧。 去软件原厂求助(https://github.com/getsentry/sentry/issues/3870),作者说我该上供啦,不要自己架服务啦,还是买原厂服务吧…… 现在,我更改了worker的配置,把这个任务单独拉出来,用六台worker伺候着,终于基本解决了问题。   顺便说一下我在Redis 2.8.7上遇到的极限值: sorted set的长度在100万左右就会性能崩溃。如果有依赖这个特性的,请大家注意有可能会引起整体系统的性能雪崩。

Posted in 默认分类 | Tagged , | 3 Comments