V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  TINGYUN  ›  全部回复第 4 页 / 共 4 页
回复总数  77
1  2  3  4  
2015-11-27 11:49:24 +08:00
回复了 TINGYUN 创建的主题 推广 [冬季福利] 部署听云 Server 基础版 ,领取卫衣! 双肩包!
@knows 今天下午 3 点前,提交礼品接收信息的, 我会安排第 1 批次把卫衣发出. 预计您下下周搬家前一定能收到。
2015-11-27 11:32:10 +08:00
回复了 TINGYUN 创建的主题 推广 [冬季福利] 部署听云 Server 基础版 ,领取卫衣! 双肩包!
@daweibao 您好,麻烦您加入听云 Server 技术交流群: 332097173 反馈,群里有技术同事 7 * 24 小时在线解答部署安装使用过程中遇到的技术问题。
有关活动或礼品发放的问题,就请邮件我啦 [email protected]
2015-11-27 10:57:11 +08:00
回复了 TINGYUN 创建的主题 推广 [冬季福利] 部署听云 Server 基础版 ,领取卫衣! 双肩包!
@hohomeil 收到邮件后都会有回复的。我会努力及时在 1-2 天内回复。
2015-11-27 07:42:21 +08:00
回复了 TINGYUN 创建的主题 推广 [冬季福利] 部署听云 Server 基础版 ,领取卫衣! 双肩包!
起床后第 1 件事就是把昨晚收到的邮件都给审核完毕啦~开心~

期待更多新同学参加!
2015-11-27 07:16:15 +08:00
回复了 TINGYUN 创建的主题 推广 [冬季福利] 部署听云 Server 基础版 ,领取卫衣! 双肩包!
@blabla
“ GitHub 同款卫衣~瑞士军刀正品背包” GitHub 卫衣采购自供应商,“同款”就是非官方的意思呀;瑞士军刀正品背包采购自京东自营。
参与完成的用户,保证都能收到帖子中保证的礼品,相当有诚意和有诚信的活动啊。
@touzl 不撕,明白就好啦~~
2015-11-26 20:07:36 +08:00
回复了 TINGYUN 创建的主题 推广 [冬季福利] 部署听云 Server 基础版 ,领取卫衣! 双肩包!
对听云 Server 的吐槽或使用体验,大家也可以 po 在这里哇
2015-11-26 19:56:30 +08:00
回复了 TINGYUN 创建的主题 推广 [冬季福利] 部署听云 Server 基础版 ,领取卫衣! 双肩包!
截止到目前,已回复完所有邮件。大家好热情!
有同学问限不限人数?是不是参与小概率中奖的活动多了,对这种这么有诚意送您礼品的不习惯?嘿嘿。
只要您是在活动期间,通过本帖 中的链接完成注册并嵌码成功的,都会送的。
期待更多的同学来玩~
2015-11-26 19:39:50 +08:00
回复了 TINGYUN 创建的主题 推广 [冬季福利] 部署听云 Server 基础版 ,领取卫衣! 双肩包!
@panxianhai 您好, [本次活动,以“应用名”对应唯一听云账号] 是说,您和您的好友们不能在同一个 Server 上部署探针。
2015-11-26 19:38:23 +08:00
回复了 TINGYUN 创建的主题 推广 [冬季福利] 部署听云 Server 基础版 ,领取卫衣! 双肩包!
@imxieke 您可以同时参加哟,不过玩法 2 您邀请的 2 个好友就不能参加领卫衣活动了
2015-11-26 14:00:39 +08:00
回复了 TINGYUN 创建的主题 推广 [冬季福利] 部署听云 Server 基础版 ,领取卫衣! 双肩包!
@r3xling 审核通过后会给大家发送选衣服尺码的表单哦~~
2015-11-26 14:00:10 +08:00
回复了 TINGYUN 创建的主题 推广 [冬季福利] 部署听云 Server 基础版 ,领取卫衣! 双肩包!
@nonozone 是这样的
@glasslion
tingyun-database.redis.py
from tingyun.api.tracert.redis_trace import wrap_redis_trace
from tingyun.api.tracert.function_trace import wrap_function_trace

rewrite_command = ['setex', 'lrem', 'zadd']
basic_command = [
'append', 'bgrewriteaof', 'bgsave', 'bitcount', 'bitop', 'bitpos', 'blpop', 'brpop', 'brpoplpush', 'client_getname',
'client_kill', 'client_list', 'client_setname', 'config_get', 'config_resetstat', 'config_rewrite', 'config_set',
'connection_pool', 'dbsize', 'debug_object', 'decr', 'delete', 'dump', 'echo', 'eval', 'evalsha', 'execute_command',
'exists', 'expire', 'expireat', 'flushall', 'flushdb', 'from_url', 'get', 'getbit', 'getrange', 'getset', 'hdel',
'hexists', 'hget', 'hgetall', 'hincrby', 'hincrbyfloat', 'hkeys', 'hlen', 'hmget', 'hmset', 'hscan', 'hscan_iter',
'hset', 'hsetnx', 'hvals', 'incr', 'incrby', 'incrbyfloat', 'info', 'keys', 'lastsave', 'lindex', 'linsert', 'llen',
'lock', 'lpop', 'lpush', 'lpushx', 'lrange', 'lrem', 'lset', 'ltrim', 'mget', 'move', 'mset', 'msetnx',
'object', 'parse_response', 'persist', 'pexpire', 'pexpireat', 'pfadd', 'pfcount', 'pfmerge', 'ping',
'pipeline', 'psetex', 'pttl', 'publish', 'pubsub', 'randomkey', 'register_script', 'rename',
'renamenx', 'response_callbacks', 'restore', 'rpop', 'rpoplpush', 'rpush', 'rpushx', 'sadd', 'save',
'scan', 'scan_iter', 'scard', 'script_exists', 'script_flush', 'script_kill', 'script_load', 'sdiff',
'sdiffstore', 'set', 'set_response_callback', 'setbit', 'setex', 'setnx', 'setrange', 'shutdown',
'sinter', 'sinterstore', 'sismember', 'slaveof', 'slowlog_get', 'slowlog_len', 'slowlog_reset',
'smembers', 'smove', 'sort', 'spop', 'srandmember', 'srem', 'sscan', 'sscan_iter', 'strlen', 'substr',
'sunion', 'sunionstore', 'time', 'transaction', 'ttl', 'type', 'unwatch', 'watch', 'zadd', 'zcard',
'zcount', 'zincrby', 'zinterstore', 'zlexcount', 'zrange', 'zrangebylex', 'zrangebyscore', 'zrank',
'zrem', 'zremrangebylex', 'zremrangebyrank', 'zremrangebyscore', 'zrevrange', 'zrevrangebyscore',
'zrevrank', 'zscan', 'zscan_iter', 'zscore', 'zunionstore']


def detect_connection (module ):
"""
:param module:
:return:
"""
if hasattr (module, 'Connection') and hasattr (module.Connection, 'connect'):
wrap_function_trace (module, "Connection.connect", name="connect")


def detect_client_operation (module ):
"""
:param module:
:return:
"""
if hasattr (module, 'StrictRedis'):
for command in basic_command:
if hasattr (module.StrictRedis, command ):
wrap_redis_trace (module, "StrictRedis.%s" % command, command )

if hasattr (module, 'Redis'):
for command in rewrite_command:
if hasattr (module.Redis, command ):
wrap_redis_trace (module, "Redis.%s" % command, command )
elif hasattr (module, 'Redis'):
for command in basic_command:
if hasattr (module.Redis, command ):
wrap_redis_trace (module, "Redis.%s" % command, command )

newrelic_database.nosql_redis.py
import newrelic.api.function_trace

_methods_1 = ['bgrewriteaof', 'bgsave', 'config_get', 'config_set',
'dbsize', 'debug_object', 'delete', 'echo', 'flushall',
'flushdb', 'info', 'lastsave', 'object', 'ping', 'save',
'shutdown', 'slaveof', 'append', 'decr', 'exists',
'expire', 'expireat', 'get', 'getbit', 'getset', 'incr',
'keys', 'mget', 'mset', 'msetnx', 'move', 'persist',
'randomkey', 'rename', 'renamenx', 'set', 'setbit',
'setex', 'setnx', 'setrange', 'strlen', 'substr', 'ttl',
'type', 'blpop', 'brpop', 'brpoplpush', 'lindex',
'linsert', 'llen', 'lpop', 'lpush', 'lpushx', 'lrange',
'lrem', 'lset', 'ltrim', 'rpop', 'rpoplpush', 'rpush',
'rpushx', 'sort', 'sadd', 'scard', 'sdiff', 'sdiffstore',
'sinter', 'sinterstore', 'sismember', 'smembers',
'smove', 'spop', 'srandmember', 'srem', 'sunion',
'sunionstore', 'zadd', 'zcard', 'zcount', 'zincrby',
'zinterstore', 'zrange', 'zrangebyscore', 'zrank', 'zrem',
'zremrangebyrank', 'zremrangebyscore', 'zrevrange',
'zrevrangebyscore', 'zrevrank', 'zscore', 'zunionstore',
'hdel', 'hexists', 'hget', 'hgetall', 'hincrby', 'hkeys',
'hlen', 'hset', 'hsetnx', 'hmset', 'hmget', 'hvals',
'publish']

_methods_2 = ['setex', 'lrem', 'zadd']

def instrument_redis_connection (module ):

newrelic.api.function_trace.wrap_function_trace (
module, 'Connection.connect')

def instrument_redis_client (module ):

if hasattr (module, 'StrictRedis'):
for method in _methods_1:
if hasattr (module.StrictRedis, method ):
newrelic.api.function_trace.wrap_function_trace (
module, 'StrictRedis.%s' % method )
else:
for method in _methods_1:
if hasattr (module.Redis, method ):
newrelic.api.function_trace.wrap_function_trace (
module, 'Redis.%s' % method )

for method in _methods_2:
if hasattr (module.Redis, method ):
newrelic.api.function_trace.wrap_function_trace (module, 'Redis.%s' % method )
@glasslion
tingyun-memcache_trace.py

import logging

from tingyun.api.tracert.memcache_node import MemcacheNode
from tingyun.api.objects.object_wrapper import wrap_object, FunctionWrapper
from tingyun.api.tracert.time_trace import TimeTrace
from tingyun.api.transaction.base import current_transaction

_logger = logging.getLogger (__name__)


class MemcacheTrace (TimeTrace ):
def __init__(self, transaction, command ):
super (MemcacheTrace, self ).__init__(transaction )
self.command = command

def create_node (self ):
return MemcacheNode (command=self.command, children=self.children, start_time=self.start_time,
end_time=self.end_time, duration=self.duration, exclusive=self.exclusive )

def terminal_node (self ):
return True


def memcached_trace_wrapper (wrapped, command ):
"""
:return:
"""
def dynamic_wrapper (wrapped, instance, args, kwargs ):
transaction = current_transaction ()
if transaction is None:
return wrapped (*args, **kwargs )

if instance is not None:
_command = command (instance, *args, **kwargs )
else:
_command = command (*args, **kwargs )

with MemcacheTrace (transaction, _command ):
return wrapped (*args, **kwargs )

def literal_wrapper (wrapped, instance, args, kwargs ):
transaction = current_transaction ()
if transaction is None:
return wrapped (*args, **kwargs )

with MemcacheTrace (transaction, command ):
return wrapped (*args, **kwargs )

if callable (command ):
return FunctionWrapper (wrapped, dynamic_wrapper )

return FunctionWrapper (wrapped, literal_wrapper )


# egg: (memcached, Client.append, get )
def wrap_memcache_trace (module, object_path, command ):
wrap_object (module, object_path, memcached_trace_wrapper, (command,))

newrelic-memcache_trace.py

import sys
import types
import time
import logging

import newrelic.core.memcache_node

import newrelic.api.transaction
import newrelic.api.time_trace
import newrelic.api.object_wrapper


_logger = logging.getLogger (__name__)


class MemcacheTrace (newrelic.api.time_trace.TimeTrace ):

node = newrelic.core.memcache_node.MemcacheNode

def __init__(self, transaction, command ):
super (MemcacheTrace, self ).__init__(transaction )

self.command = command

def dump (self, file ):
print >> file, self.__class__.__name__, dict (command=self.command )

def create_node (self ):
return self.node (command=self.command, children=self.children,
start_time=self.start_time, end_time=self.end_time,
duration=self.duration, exclusive=self.exclusive )

def terminal_node (self ):
return True

class MemcacheTraceWrapper (object ):

def __init__(self, wrapped, command ):
if isinstance (wrapped, tuple ):
(instance, wrapped ) = wrapped
else:
instance = None

newrelic.api.object_wrapper.update_wrapper (self, wrapped )

self._nr_instance = instance
self._nr_next_object = wrapped

if not hasattr (self, '_nr_last_object'):
self._nr_last_object = wrapped

self._nr_command = command

def __get__(self, instance, klass ):
if instance is None:
return self
descriptor = self._nr_next_object.__get__(instance, klass )
return self.__class__((instance, descriptor ), self._nr_command )

def __call__(self, *args, **kwargs ):
transaction = newrelic.api.transaction.current_transaction ()

if not transaction:
return self._nr_next_object (*args, **kwargs )

if callable (self._nr_command ):
if self._nr_instance is not None:
command = self._nr_command (self._nr_instance, *args,
**kwargs )
else:
command = self._nr_command (*args, **kwargs )
else:
command = self._nr_command

with MemcacheTrace (transaction, command ):
return self._nr_next_object (*args, **kwargs )

def memcache_trace (command ):
def decorator (wrapped ):
return MemcacheTraceWrapper (wrapped, command )
return decorator

def wrap_memcache_trace (module, object_path, command ):
newrelic.api.object_wrapper.wrap_object (module, object_path,
MemcacheTraceWrapper, (command,))
@glasslion 同学提抄袭这词有些严肃啊,那就认真的回复下这个质疑(明明上面的回复也很认真)@Strikeactor @beordle @zjxubinbin @alsotang
首先,听云绝对不会抄袭的。
听云和 New Relic 都是基于第三方的开源模块 wrapt ,加自主研发。两家都大量借用了 wrapt, requests 模块相关功能。某一语言的探针的监测原理是一样的,基于开源组件,部分源码有重叠,是自然的事情。

下面是三段听云和 New Relic 核心代码的对照。

tingyun-hooks_entrance.py
//i.v2ex.co/d0c12Dn1.png
//i.v2ex.co/TVII4A7b.png

newrelic-weib_transaction.py
import sys
import cgi
import base64
import time
import string
import re
import json
import logging

try:
import urlparse
except ImportError:
import urllib.parse as urlparse

import newrelic.packages.six as six

import newrelic.api.application
import newrelic.api.transaction
import newrelic.api.object_wrapper
import newrelic.api.function_trace
_logger = logging.getLogger (__name__)

_rum_header_fragment = '<script type="text/javascript">' \
'var NREUMQ=NREUMQ||[];NREUMQ.push (["mark","firstbyte",' \
'new Date ().getTime ()]);</script>'

_rum_footer_short_fragment = '<script type="text/javascript">' \
'if (!NREUMQ.f ){NREUMQ.f=function (){NREUMQ.push (["load",' \
'new Date ().getTime ()]);if (NREUMQ.a )NREUMQ.a ();};' \
'NREUMQ.a=window.onload;window.onload=NREUMQ.f;};' \
'NREUMQ.push (["nrf2","%s","%s","%s","%s",%d,%d,' \
'new Date ().getTime ()]);</script>'

_rum2_footer_short_fragment = '<script type="text/javascript">' \
'if (!NREUMQ.f ){NREUMQ.f=function (){NREUMQ.push (["load",' \
'new Date ().getTime ()]);if (NREUMQ.a )NREUMQ.a ();};' \
'NREUMQ.a=window.onload;window.onload=NREUMQ.f;};' \
'NREUMQ.push (["nrfj","%s","%s","%s","%s",%d,%d,' \
'new Date ().getTime (),"%s","%s","%s","%s","%s"]);</script>'

_rum_footer_long_fragment = '<script type="text/javascript">' \
'if (!NREUMQ.f ){NREUMQ.f=function (){NREUMQ.push (["load",' \
'new Date ().getTime ()]);var e=document.createElement ("script");' \
'e.type="text/javascript";' \
'e.src=(("http:"===document.location.protocol )?"http:":"https:")' \
'+"//"+"%s";document.body.appendChild (e );if (NREUMQ.a )NREUMQ.a ();};' \
'NREUMQ.a=window.onload;window.onload=NREUMQ.f;};' \
'NREUMQ.push (["nrf2","%s","%s","%s","%s",%d,%d,' \
'new Date ().getTime ()]);</script>'

_rum2_footer_long_fragment = '<script type="text/javascript">' \
'if (!NREUMQ.f ){NREUMQ.f=function (){NREUMQ.push (["load",' \
'new Date ().getTime ()]);var e=document.createElement ("script");' \
'e.type="text/javascript";' \
'e.src=(("http:"===document.location.protocol )?"http:":"https:")' \
'+"//"+"%s";document.body.appendChild (e );if (NREUMQ.a )NREUMQ.a ();};' \
'NREUMQ.a=window.onload;window.onload=NREUMQ.f;};' \
'NREUMQ.push (["nrfj","%s","%s","%s","%s",%d,%d,' \
'new Date ().getTime (),"%s","%s","%s","%s","%s"]);</script>'

# Seconds since epoch for Jan 1 2000

JAN_1_2000 = time.mktime ((2000, 1, 1, 0, 0, 0, 0, 0, 0 ))

def _encode (name, key ):
s = []

# Convert name and key into bytes which are treated as integers.

key = list (six.iterbytes (six.b (key )))
for i, c in enumerate (six.iterbytes (six.b (name ))):
s.append (chr (c ^ key[i % len (key )]))
return s

if six.PY3:
def obfuscate (name, key ):
if not (name and key ):
return ''

# Always pass name and key as str to _encode ()

return str (base64.b64encode (six.b (''.join (_encode (name, key )))),
encoding='Latin-1')
else:
def obfuscate (name, key ):
if not (name and key ):
return ''

# Always pass name and key as str to _encode ()

return base64.b64encode (six.b (''.join (_encode (name, key ))))

def deobfuscate (name, key ):
if not (name and key ):
return ''

# Always pass name and key as str to _encode ()

return ''.join (_encode (six.text_type (base64.b64decode (six.b (name )),
encoding='Latin-1'), key ))

def _lookup_environ_setting (environ, name, default=False ):
flag = environ.get (name, default )
if default is None or default:
try:
flag = not flag.lower () in ['off', 'false', '0']
except AttributeError:
pass
else:
try:
flag = flag.lower () in ['on', 'true', '1']
except AttributeError:
pass
return flag

def _extract_token (cookie ):
try:
t = re.search (r"\bNRAGENT=(tk=.{16})", cookie )
token = re.search (r"^tk=([^\"<'>]+)$", t.group (1 )) if t else None
return token and token.group (1 )
except Exception:
pass


class WebTransaction (newrelic.api.transaction.Transaction ):

def __init__(self, application, environ ):

# The web transaction can be enabled/disabled by
# the value of the variable "newrelic.enabled"
# in the WSGI environ dictionary. We need to check
# this before initialising the transaction as needs
# to be passed in base class constructor. The
# default is None, which would then result in the
# base class making the decision based on whether
# application or agent as a whole are enabled.

enabled = _lookup_environ_setting (environ, 'newrelic.enabled', None )

# Initialise the common transaction base class.

newrelic.api.transaction.Transaction.__init__(self, application, enabled )

# Bail out if the transaction is running in a
# disabled state.

if not self.enabled:
return

# Will need to check the settings a number of times.

settings = self._settings

# Check for override settings from WSGI environ.

self.background_task = _lookup_environ_setting (environ, 'newrelic.set_background_task', False )

self.ignore_transaction = _lookup_environ_setting (environ,
'newrelic.ignore_transaction', False )
self.suppress_apdex = _lookup_environ_setting (environ,
'newrelic.suppress_apdex_metric', False )
self.suppress_transaction_trace = _lookup_environ_setting (environ,
'newrelic.suppress_transaction_trace', False )
self.capture_params = _lookup_environ_setting (environ,
'newrelic.capture_request_params',
settings.capture_params )
self.autorum_disabled = _lookup_environ_setting (environ,
'newrelic.disable_browser_autorum',
not settings.browser_monitoring.auto_instrument )

# Extract from the WSGI environ dictionary
# details of the URL path. This will be set as
# default path for the web transaction. This can
# be overridden by framework to be more specific
# to avoid metrics explosion problem resulting
# from too many distinct URLs for same resource
# due to use of REST style URL concepts or
# otherwise.

request_uri = environ.get ('REQUEST_URI', None )
script_name = environ.get ('SCRIPT_NAME', None )
path_info = environ.get ('PATH_INFO', None )
http_cookie = environ.get ('HTTP_COOKIE', None )

if http_cookie and ("NRAGENT" in http_cookie ):
self.rum_token = _extract_token ( http_cookie )
self.rum_trace = True if self.rum_token else False

self._request_uri = request_uri

if self._request_uri is not None:
# Need to make sure we drop off any query string
# arguments on the path if we have to fallback
# to using the original REQUEST_URI. Can't use
# attribute access on result as only support for
# Python 2.5+.

self._request_uri = urlparse.urlparse (self._request_uri )[2]

if script_name is not None or path_info is not None:
if path_info is None:
path = script_name
elif script_name is None:
path = path_info
else:
path = script_name + path_info

self.set_transaction_name (path, 'Uri', priority=1 )

if self._request_uri is None:
self._request_uri = path
else:
if self._request_uri is not None:
self.set_transaction_name (self._request_uri, 'Uri', priority=1 )

# See if the WSGI environ dictionary includes the
# special 'X-Request-Start' or 'X-Queue-Start' HTTP
# headers. These header are optional headers that can be
# set within the underlying web server or WSGI server to
# indicate when the current request was first received
# and ready to be processed. The difference between this
# time and when application starts processing the
# request is the queue time and represents how long
# spent in any explicit request queuing system, or how
# long waiting in connecting state against listener
# sockets where request needs to be proxied between any
# processes within the application server.
#
# Note that mod_wsgi sets its own distinct variables
# automatically. Initially it set mod_wsgi.queue_start,
# which equated to when Apache first accepted the
# request. This got changed to mod_wsgi.request_start
# however, and mod_wsgi.queue_start was instead used
@lins05 谢谢反馈,已改成 copy
@zjxubinbin 听云的 Python 探针是在开源的基础上、公司内部数据协议上自行研发的,对于一些其他框架、插件的开发,将会逐渐的支持。
另据研发同学的可靠消息,对 Tornado , flask 的支持,很快就会实现啦。
@glasslion https://pypi.python.org/pypi/tingyun 这里这里
非常感谢关注听云,欢迎提出更多宝贵的意见。
1  2  3  4  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4750 人在线   最高记录 6543   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 19ms · UTC 09:56 · PVG 17:56 · LAX 02:56 · JFK 05:56
Developed with CodeLauncher
♥ Do have faith in what you're doing.