Metadata-Version: 1.0
Name: wlreporter
Version: 0.2.0
Summary: create report for wsgi_lineprof.
Home-page: https://github.com/denzow/wsgi_lineprof_reporter
Author: denzow
Author-email: denzow@gmail.com
License: MIT License

Copyright (c) 2017 satoshi yamada

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Description: wsgi_lineprof_reporter
        ============================
        
        このスクリプトは[wsgi_lineprof](https://github.com/ymyzk/wsgi_lineprof)で出力した複数の結果を統合し、サマリー的なレポートを出力します。
        [wsgi_lineprof](https://github.com/ymyzk/wsgi_lineprof)では1リクエストごとにログが出力されますので、大量にアクセスしたあとでは確認量が多く時間がかかっていました。
        そのためすべての結果を集計してシンプルなレポートを出力します。
        
        
        前提
        ------------
        
        本スクリプトはPython3.xの標準ライブラリのみで作成しているので依存ライブラリはありません。
        
        インストール
        ------------
        
        ### PyPi
        
        以下のコマンドで簡単にインストールできます。インストールに成功すれば`wlreporter`コマンドが利用可能になります。
        
        ```
        $ pip install wlreporter
        $ wlreporter -v
        ```
        
        ### 手動
        
        単一のスクリプトで動作します。以下を実行し、任意のディレクトリに配置して利用可能です。
        
        ```
        $ curl https://raw.githubusercontent.com/denzow/wsgi_lineprof_reporter/master/wlreporter/wlreporter.py > wlreporter.py
        ```
        
        使い方
        -----------
        
        
        
        [wsgi_lineprof](https://github.com/ymyzk/wsgi_lineprof)で対象のアプリケーションのプロファイルログを取得します。。
        取得したファイルに対して以下のように実行します。
        
        ```bash
        $ wlreporter -f /target/file/name.log
        ```
        
        実行すると以下の2つのファイルが元ファイルと同じディレクトリに生成されます。
        
        * `xxxx_summary_data.log`
        * `xxxx_line_data.log`
        
        それぞれ以下のような内容です。
        
        
        #### `xxxx_summary_data.log`
        
        関数単位での実行時間順に集計した結果です。上位の関数にチューニングポイントがあります。
        
        ```
                                 file_name       func_name  total_time  avg_time  call_count
        ----------------------------------  --------------  ----------  --------  ----------
        /home/XXXXXXXXXXXXXX/python/app.py  get_index          0.17849   0.17849           1
        /home/XXXXXXXXXXXXXX/python/app.py  get_initialize         0.0       0.0           1
        
        ```
        
        #### `xxxx_line_data.log`
        
        スクリプト内の各行ごとにかかった時間やHit等を集計しています。
        
        
        ```
                                 file_name  line  min_time  max_time  avg_per_time  hits  total_time       graph                                                                                                         code
        ----------------------------------  ----  --------  --------  ------------  ----  ----------  ----------  -----------------------------------------------------------------------------------------------------------
        /home/XXXXXXXXXXXXXX/python/app.py   192         0         0             0     0           0              @app.route('/')                                                                                            
        /home/XXXXXXXXXXXXXX/python/app.py   193         0         0             0     0           0              def get_index():                                                                                           
        /home/XXXXXXXXXXXXXX/python/app.py   194     191.0     191.0         191.0     1         191                  page = int(request.args.get('page', 0))                                                                
        /home/XXXXXXXXXXXXXX/python/app.py   195    2333.0    2333.0        2333.0     1        2333                  cur = db().cursor()                                                                                    
        /home/XXXXXXXXXXXXXX/python/app.py   196       1.0       1.0           1.0     1           1                  cur.execute("""                                                                                        
        /home/XXXXXXXXXXXXXX/python/app.py   197         0         0             0     0           0                  SELECT                                                                                                 
        /home/XXXXXXXXXXXXXX/python/app.py   198         0         0             0     0           0                  id,                                                                                                    
        /home/XXXXXXXXXXXXXX/python/app.py   199         0         0             0     0           0                  name,                                                                                                  
        /home/XXXXXXXXXXXXXX/python/app.py   200         0         0             0     0           0                  image_path,                                                                                            
        /home/XXXXXXXXXXXXXX/python/app.py   201         0         0             0     0           0                  price,                                                                                                 
        /home/XXXXXXXXXXXXXX/python/app.py   202         0         0             0     0           0                  description,                                                                                           
        /home/XXXXXXXXXXXXXX/python/app.py   203         0         0             0     0           0                  created_at                                                                                             
        /home/XXXXXXXXXXXXXX/python/app.py   204         0         0             0     0           0                  FROM                                                                                                   
        /home/XXXXXXXXXXXXXX/python/app.py   205         0         0             0     0           0                      products p                                                                                         
        /home/XXXXXXXXXXXXXX/python/app.py   206         0         0             0     0           0                  ORDER BY  id DESC LIMIT 50 OFFSET {}                                                                   
        /home/XXXXXXXXXXXXXX/python/app.py   207    2580.0    2580.0        2580.0     1        2580                  """.format(page * 50))                                                                                 
        /home/XXXXXXXXXXXXXX/python/app.py   208       9.0       9.0           9.0     1           9                  products = cur.fetchall()                                                                              
        /home/XXXXXXXXXXXXXX/python/app.py   209      54.0      54.0          54.0     1          54                  product_id_list = [str(x["id"]) for x in products]                                                     
        /home/XXXXXXXXXXXXXX/python/app.py   210   29017.0   29017.0       29017.0     1       29017  **              comment_data = get_all_comments(product_id_list)                                                       
        /home/XXXXXXXXXXXXXX/python/app.py   212     0.529     0.529         0.529    51          27                  for product in products:                                                                               
        /home/XXXXXXXXXXXXXX/python/app.py   213      1.28      1.28          1.28    50          64                      product['description'] = product['description'][:70]                                               
        /home/XXXXXXXXXXXXXX/python/app.py   214      3.66      3.66          3.66    50         183                      product['created_at'] = to_jst(product['created_at'])                                              
        /home/XXXXXXXXXXXXXX/python/app.py   215       1.0       1.0           1.0    50          50                      product['comments'] = comment_data.get(product['id'])[:5]#get_comments(product['id'])              
        /home/XXXXXXXXXXXXXX/python/app.py   216      0.84      0.84          0.84    50          42                      product['comments_count'] = len(comment_data.get(product['id'])) #get_comments_count(product['id'])
        /home/XXXXXXXXXXXXXX/python/app.py   218  143940.0  143940.0      143940.0     1      143940  !*********      return render_template('index.html', products=products, current_user=current_user())                   
        /home/XXXXXXXXXXXXXX/python/app.py   278         0         0             0     0           0              @app.route('/initialize')                                                                                  
        /home/XXXXXXXXXXXXXX/python/app.py   279         0         0             0     0           0              def get_initialize():                                                                                      
        /home/XXXXXXXXXXXXXX/python/app.py   280    9837.0    9837.0        9837.0     1        9837  *               cur = db().cursor()                                                                                    
        /home/XXXXXXXXXXXXXX/python/app.py   281     284.0     284.0         284.0     1         284                  cur.execute('DELETE FROM users WHERE id > 5000')                                                       
        /home/XXXXXXXXXXXXXX/python/app.py   282     225.0     225.0         225.0     1         225                  cur.execute('DELETE FROM products WHERE id > 10000')                                                   
        /home/XXXXXXXXXXXXXX/python/app.py   283    3487.0    3487.0        3487.0     1        3487                  cur.execute('DELETE FROM comments WHERE id > 200000')                                                  
        /home/XXXXXXXXXXXXXX/python/app.py   284    4803.0    4803.0        4803.0     1        4803                  cur.execute('DELETE FROM histories WHERE id > 500000')                                                 
        /home/XXXXXXXXXXXXXX/python/app.py   285         0         0             0     1           0                  return ("Finish")                                                                                      
        
        ```
        
        graph列はレポート内の最大時間との比率を`*`で表現しています。`*`が多い行にチューニングポイントがあります。最も時間を要している行には`!`がついているのでまずはその行が改善できないかを検討します。
        
        各列の意味は以下です。
        
        |column|mean|
        |--------|------|
        |file_name|File Name|
        |line|Line Number |
        |min_time|Min Time(per One hit)for the line |
        |max_time|Max Time(per One hit)for the line |
        |avg_per_time|Avg Time(per One hit)for the line |
        |hits| hit count the line |
        |total_time| Avg * Hits |
        
        #### excludeオプション
        
        最も時間がかかっている箇所をチューニングするのが原則ですが、そのポイントを諦める場合に使用します。
        
        ```sh
        $ python wlreporter.py -f sample/small.log  -e /home/XXXXXXXXXXXXXX/python/app.py:218
        ```
        
        本例の場合、`/home/XXXXXXXXXXXXXX/python/app.py`の218行目はGraph列の計算に含まれなくなります。
        
        ```
                                 file_name  line  min_time  max_time  avg_per_time  hits  total_time       graph                                                                                                         code
        ----------------------------------  ----  --------  --------  ------------  ----  ----------  ----------  -----------------------------------------------------------------------------------------------------------
        :
        /home/XXXXXXXXXXXXXX/python/app.py   209      54.0      54.0          54.0     1          54                  product_id_list = [str(x["id"]) for x in products]                                                     
        /home/XXXXXXXXXXXXXX/python/app.py   210   29017.0   29017.0       29017.0     1       29017  !*********      comment_data = get_all_comments(product_id_list)                                                       
        /home/XXXXXXXXXXXXXX/python/app.py   212     0.529     0.529         0.529    51          27                  for product in products:                                                                               
        /home/XXXXXXXXXXXXXX/python/app.py   213      1.28      1.28          1.28    50          64                      product['description'] = product['description'][:70]                                               
        /home/XXXXXXXXXXXXXX/python/app.py   214      3.66      3.66          3.66    50         183                      product['created_at'] = to_jst(product['created_at'])                                              
        /home/XXXXXXXXXXXXXX/python/app.py   215       1.0       1.0           1.0    50          50                      product['comments'] = comment_data.get(product['id'])[:5]#get_comments(product['id'])              
        /home/XXXXXXXXXXXXXX/python/app.py   216      0.84      0.84          0.84    50          42                      product['comments_count'] = len(comment_data.get(product['id'])) #get_comments_count(product['id'])
        /home/XXXXXXXXXXXXXX/python/app.py   218  143940.0  143940.0      143940.0     1      143940  @IGNORE@        return render_template('index.html', products=products, current_user=current_user())                   
        /home/XXXXXXXXXXXXXX/python/app.py   278         0         0             0     0           0              @app.route('/initialize')   
        
        ```
        
        最も時間がかかっているのは218行目ですが、Graph列は`@IGNORE@`と表示されています。次に時間がかかっている210行目を最大時間としてGraph列が計算されるため、突出して時間がかかっている行のチューニングを諦めた場合の次のチューニングポイントが探しやすくなります。
        
        #### その他オプション
        
        
        幾つかオプションがあります。
        
        ```
        denzownoMacBook-Pro:wsgi_lineprof_reporter denzow$ python wlreporter.py --help
        usage: wlreporter.py [-h] [-v] -f TARGET_FILE [-d DB_NAME]
                             [-r REPORT_NAME_PREFIX]
                             [-e [EXCLUDE_PATTERNS [EXCLUDE_PATTERNS ...]]]
        
        This script is parsing wsgi_lineprof result
        
        optional arguments:
          -h, --help            show this help message and exit
          -v, --version         show program's version number and exit
          -f TARGET_FILE, --file TARGET_FILE
                                target line profiler log file
          -d DB_NAME, --db-name DB_NAME
                                db name for persistence. if not set, use :memory:.
                                :memory: is temporary database.
          -r REPORT_NAME_PREFIX, --report-name-prefix REPORT_NAME_PREFIX
                                report name prefix. if not set, use profile log name.
          -e [EXCLUDE_PATTERNS [EXCLUDE_PATTERNS ...]], --exclude [EXCLUDE_PATTERNS [EXCLUDE_PATTERNS ...]]
                                exclude patterns for line_data. ie.) app.py:120. ->
                                app.py 120 row's total time is ignore.
        ```
        
        内部では一旦オンメモリのSQLiteに格納してからレポートティングしています。そのためレポート後は自動で
        DBは破棄されますが`-d`でファイル名を指定することで分析結果を格納したDBをスクリプト終了後も保持することができます。
        
        通常のレポート以上の分析を実施する場合はこちらのDBをSQLiteに対応したクライアントで分析してください。
        
        謝辞
        ---------
        
        本スクリプトは[wsgi_lineprof](https://github.com/ymyzk/wsgi_lineprof)を前提としています。[wsgi_lineprof](https://github.com/ymyzk/wsgi_lineprof)の作者のymyzk様の
        お陰でWEBアプリのパフォーマンスチェックが快適になりました。
        
        TODO
        ------------
        
        * 複数レポートの差分比較
        * HTML でのグラフィカルレポートの追加
        * パフォーマンス改善
Platform: UNKNOWN
