Tag Archives: Python

在Python里抑制requests库的日志消息

我自己经常在自己的脚本开头使用logging.basicConfig(level=logging.DEBUG)初始化logging库,但是随之而来的就是requests会输出大量日志,甚至盖过了我自己的内容。所以我打算抑制requests的日志。 综合搜索的资料,和探索源代码,我发现: 所以只需要在basicConfig后面加一句 logging.getLogger(“urllib3”).setLevel(logging.WARNING) 就可以抑制这部分日志了。 另外,logging.Logger.manager.loggerDict是个好东西,可以检查当前到底存在哪些logger。通过检查loggerDict[‘urllib3.connectionpool’].propagate发现其为True,其上层也是True,因此,虽然这两层logger一个没handler,一个NullHandler,但是该logger记录的日志消息仍会逐层上传,最终被basicConfig的root logger处理。

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

内训教程释出:from CGI to WSGI

古代时,读过MSDN里的《from C++ to COM》受益匪浅。后来学习Python,自己也写了一篇 from CGI to WSGI 的教程,用于内部培训。 https://julyclyde.org/pyCGIWSGI.pdf

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

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 参考资料:https://pythonhosted.org/setuptools/pkg_resources.html #!/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 … Continue reading

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

某种情况下PATH不生效的问题

今天遇到的情况,我同事在 virtualenv 里执行 gunicorn 运行 Django app,出来的 Django 错误信息却是 /usr/local/ 路径下面那套 Python 里的: 2015-09-18 15:32:47 [11538] [ERROR] Error handling request Traceback (most recent call last): File “/usr/local/lib/python2.7/dist-packages/gunicorn/workers/sync.py“, line 131, in handle_request respiter = self.wsgi(environ, resp.start_response) File “/usr/local/lib/python2.7/dist-packages/django/core/handlers/wsgi.py“, line 236, in … Continue reading

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

Python import语句一处文档和行为不一致的问题

话说,我团运维组曾一度极为昌盛,可惜后来公司里有人搞政治,好好的一个组被拆成了好几个,后来竟分属不同大部门,工作关系也开始形同陌路。考虑到丰富经验,换换方向,今年7月,我转岗到了运维开发组,开始真正把Python作为职业。 无奈,基础差,还得学,8月18日,这么吉利的一天,我第n次在看Python的文档,看着看着,注意到一些标准库的名字是带点的,比如os.path,再比如logging.config等。然后就开始help各个模块,发现os.path的名字居然不叫path而是posixpath!!再看,logging是个package,但os居然是个module!! 于是,问题就来了: Python语言里对 import A.B.C 这种形式是这么规定的: when using syntax like import item.subitem.subsubitem, each item except for the last must be a package; the last item can be a module or a package but can’t be a class or function … Continue reading

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

PHP——成也Web,败也Web

早年我并不知道Python写的Web应用是怎么部署的,总觉得像PHP、ASP一样,仅仅提供一个语言级别的执行模块,直接嵌入Web服务器运行,甚至于直接对外提供带扩展名的URL都是自然而然的事情。 前一阵学习了Python,总是如别人一样,不自觉的和PHP进行对比。随着学习的逐步深入,更发现PHP的发展受限于其Web出身,恐将来难成正经的通用开发语言。 先说说PHP从历史到今天分别是怎么运行的: CGI SAPI:由Web服务器的请求处理进程fork+exec这个CGI,用环境变量单向传递Headers给应用程序,应用程序从stdin读request body,其stdout会被Web服务器作为response headers和body输出给客户端。Web服务器和PHP CGI的关系是父子进程间通信,当应用程序的响应速度较慢时(忙于计算、外部I/O等),会阻塞对应的Web服务器进程/线程;而且因为CGI进程的输入之一是环境变量,导致其只能一次性使用,需要承担频繁fork()的开销 Apache2Handler等Web服务器模块类SAPI:Apache2调用Handler,并向其传递server_context;Handler利用server_context读到Header、body等内容之后构造起PHP脚本的context,也就是$_SERVER之类,然后开始执行PHP脚本。而脚本执行的输出也直接被Web服务器收集。PHP和Web服务器在同一个进程内执行,处于完全从属的地位。Apache2Handler方式本来可以通过多线程等方式让Apache不受PHP阻塞,但由于PHP社区的坚持,目前常见的Apache2+PHP配置都是prefork MPM的,以至于性能相当一般 FastCGI SAPI:原理是把每次fork处理一个请求,变成一个不退出的循环,每循环一轮就处理一个请求;多进程listen在同一个socket上,实现并发。PHP-FPM是后来的一个民间补丁项目,提供了更好的进程管理和附加功能,处理完License问题之后,FPM合并入PHP主线。Web服务器和PHP的关系是网络通信的关系,可以分离到不同服务器上部署。Web服务器可以使用多线程或者I/O复用技术来处理别的请求,性能会>比前二者好很多。但FPM的实现仍然不够好。 PHP的各种扩展模块,大都是偏重于数据库、字符串处理等应用层面的功能,充分体现了其作为Web开发利器的特点;而对于I/O、POSIX基础功能,则关注较少。 Web开发之外的领域,用PHP写独立运行的服务器,并不是主流的用法。其依赖的pcntl、pthread等模块也是较晚加进来的,而且也没啥人用。用 PEAR Net_Server(无人维护) 通用服务器框架 PEAR HTTP2 协议解析库 PEAR HTTP_Server(无人维护) HTTP服务器框架 PECL pecl_HTTP(需要编译) HTTP服务器框架,我没编译出来…… 等几个模块,可以很容易的创作一个独立的HTTP静态文件或SOA服务;但如果想在其中运行一般的Web PHP脚本,就需要用PHP本身实现一个SAPI: 输入方面是很容易仿制的,除了需要构造Predefined/Reserved Variables(感谢作者,$_SERVER等只是普通数组,只须填充内容即可)再写一下上传文件自动存盘的处理就差不多了 输出方面就比较麻烦,因为“写”是动作,是对外部环境施加影响的。考虑到PHP深深的“假设stdout”情结,须使用output buffer机制收集输出内容,副作用是chunked输出的程序会丧失分段效果,变成缓冲输出;header()函数需要用pecl-apd扩展中的override_function()函数,但此扩展却无法兼容当前的ZendAPI,无法编译成功 传统上,协议解析是SAPI的工作,PHP的语言引擎部分从属于SAPI,以至于没积累出什么好用的协议解析库;而PHP书写的Web应用向来都是直接调用SAPI提供的函数,积习难改,以至于想用PHP本身来写Web服务器就须要去实现一个SAPI,但又会遭遇到这个语言本身的Web烙印太深、假设太多,以及不够“动态”的问题。 传统上,网络通信向来也不须由PHP来处理,以至于也没积累出什么好用的通信框架库。 最终,PHP成了一个Web only的开发语言。真可谓成也Web、败也Web。 一些新兴的PECL扩展提供了高性能I/O框架,比如swoole,但因为上述SAPI的问题,目前尚未出现像Python那样用PHP本身写的PHP Web服务器。 相比之下,不用写<%的Python语言,其发展道路就更加general purpose一些: … Continue reading

Posted in 默认分类 | Tagged | 1 Comment