From c59ba98e8c8416d1d0821fbb402c8018a9f53c8a Mon Sep 17 00:00:00 2001 From: xiaji Date: Fri, 29 Aug 2025 21:33:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BF=AE=E6=94=B9=E6=B5=8B?= =?UTF-8?q?=E8=AF=95Gunicorn=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __pycache__/gunicorn_tab.cpython-38.pyc | Bin 15777 -> 16501 bytes __pycache__/threads.cpython-38.pyc | Bin 24474 -> 24893 bytes app.log | 59 ++++++++++ gunicorn_tab.py | 40 ++++++- threads.py | 148 +++++++++++++++++++++--- 5 files changed, 222 insertions(+), 25 deletions(-) diff --git a/__pycache__/gunicorn_tab.cpython-38.pyc b/__pycache__/gunicorn_tab.cpython-38.pyc index a7cbbd5202ee2b3b07a56b8c91db9c14fbeb4225..bed53daf84b662643bedd7b4a78a977414915efb 100644 GIT binary patch delta 1415 zcmZ`&U2GIp6u#%~?Cf;^sQsfYr9T!eEp=N!i&;>c*3=M!AwgdhgN;M0TT^g$78{+m zkk)pUmKJYW2yNpmSOkJ>aif*#4+f)R()id!UVN}KyG=Aa5g+l~ozgTxXL9b`d(L;i zbLRZSKYnlm{IC0bZi3IuwVUsjwcYfmi{NkePf?qzK9G5nZN+6`uM-i?DB%TMWYP^9 z2@$1H&3t9(qe>&!glNd23vsloPF<9#21(=8 zrW)0=;zV_aJRMZIjwkOLkMj@`_524B55ia%gs~QcPXkWg=|@L?S>$_}97NX#Nww=$ z1jX z4kgRCGyxn=zVnoq!h6YMRV@Hoa%QVSVIWzvZ9l+O)>XUDBO<2*CHyq7-)ax^s-o_a zof?c2S=0n9V5VeI2uXU@s@^r};Mql;8^wd-Me(8d+nt}1D0!K@_))c-gzd_TZsQ!Zn5Fz`3WqlH8E^{7B@$S zQ(sL7%OiX)R*iu`pe0g;XKUixYB{6RYu$pKgVnQd+hngriV^%c@=;%9TI&9sZ^(>} zo3qCL*3Q=UBkTvr7gNUV)Rj}82UJ*Mk-#n}VsF=!iFOaLyMgUu7ZPJ9JP5Rl-(Yu! z8(7`8f-+hFc@8fKM0hAr3A_#oTYd?l{)CFObE!MpV2bib6a;Ja;CdNsJ7(R}5 zT-_@}I6t6MdtOur*51Xd!V6F>v7PnL_9{BMzbAwv^3g~OZmp5g)R#CA7t)Ct>(UA9 ziz#lhGh>}kn4_1iuP>$IH+XiVF>`t{ojAKPIBdnHA59IPxX4SK3a@{A%EY{mB+`BJ zsSEu!&C0nOD;Ljmf|1e9=*%9O$4M~4#@f=Xd(QWdet1N|De14@QSv)aQ691ro|VkF zISStR67UgHA2KT}*Y$3QwAY?2lj*{K}r(9fIfC@a`p;XY!ur+>KJcy5fg~~zWto-zK1k3*gbzV!YESu)^;q}rL(~l4cjlh^-S5n~XZ0ea&b#(! zW;zIeqIV`v9%!9#Ef>Of{8iX)4@YW(A^wIft*BV-mAIyvBW7^YNDr7qCx$PcjvDk) z!zVI2)up7I68jMAj4LMTgC1(qq?#geo6b6@q4SkJtq=~K|2TC1b#R!F0wA5TMp}po zEu_Rx_ZY*xv_He}^I&WiBofA2L17Gd%kiK$Qjo`&gv#Ismm~uN7clAuH?_vR^8n2_ z=o^KowXv)T;1JIG^R2=4s{n1*mGbQX=d4-3LE)0Mb;E9eQ5*>D%22qU=svbWa3Qd_ zHvzhuv}uY7*NLXyl*Etn9OJNaI5;vmoF|m4Btf>3!>%61RFavRO(;)aNI=rX+gD9h zBOT1R|DT4XHMd0D@ip89^ej_0!BqR?1RTxgnX+u%o%HmRerTtfU2|McAK@F>$($4c zKARSPNjP1Vi=`EXSz@>)i+F8e>P9da+`bRzE8_4DqoEqe#lcW9J`4qLC3IZ92fW1) zHdf~s`+u%OvJt9*$CGXFle{fySjj^%~Ok diff --git a/__pycache__/threads.cpython-38.pyc b/__pycache__/threads.cpython-38.pyc index 0d5e2fbdf160e6b0c130fd26b21110a6da43e049..fb045f6509981d3b1eb178d875e9f0e7516f3f29 100644 GIT binary patch delta 5479 zcmbtYYfw|!9nZ;&kc1eXhL=RtRX{;eaK#lB6n2q^7<4h95t0jB3`u(KjS3SMtn0I` zW%tZF&Tb!2we7YuyTj7i&a7JM&VFiVI@5lzZ9ddK#K)(dPIo$Or!)5dKf&B2B-L%1 z;Wy`=$36ew@BAO<=Dm4x=QCnCn2}*r!QaG>W``PyIC%9$G zw~D-(X$&&hbINdOBp0?SGtx}lF$*CZK?@Mp_WS)_x(=U95zGixIFjLVxxIaoT2FzfgL3X zc7&AxVKWt^pjUQLq0ci)S7E;zVKuvIE?Kz-n`;r!&a!lxj_oppa)b(Y&%DCKXKlde zN>rXyvFwbFo+O;VDCBwXRvda6fg7=k)n~48;i9mizfTf;eIo*0jl+sm&(#5^Zv$`| z1AP>H$mJ0!U8>8y%(lupoSTg5b{Ob~e}$@^ZU?I0e<_A0E^Y+)KodC&&J8<&Y;pEh zLh9MUobCyQG9ObYE1V+aab@Jdu@%qrgQe$XxIzn+YXqwf`C66paF%27}St)JEE$%*LB8a-T8Gk6BojX8x+j_d0oF+ zUxe*?)>2VTI@x%|a=imPcFw(5aez#4OA-X+ixr*t;c@}FVo`a+T3H+s!aB(-2!TkB zj)qHeHDlD{8Eb<|3zqOQWyD(Bhg6%`uC{!3b3=2*A+%FTV+Hug>uoo*y5nSFQ>CSB zKDUPXDi4|tg1KK+R;l|H-$+J{vrod0m*hlV#^foBxi+myQhBd$a->cc9))5eqU1;I%}0O5357zwxrM6VFmN&|8rOa_vaVtei3 z)Itrzdww1#l%5VpV}NWu5O?B7x85T5h|i-Dy$BI8j$pGBVJF+RZ8Pa(Bil~5{vQ#L z;2hC(++NqUF{SfLV8CCIO-xMdsKgI`_4Y$?T+VKPO`Tbf7RS5 zS5LHYn@Uxnq6cHA$-*g_#$85#v(uOQq-9uJ451hs5;A*RTg<+(>uO@DLjB@O6?Q-% zH1EC$rGfpraZ>{x_E>H}3~aWGKCs;DwGRe;qT5e>+$ZP(m=MHJYhLVL`0(Q6>t`Qc zm|D2>-8wsUuswUWlOUVi<4c+fV#7J?<3jdlXewJkFw{rxqjVN}R|2t*Kt~yyy{FXG;-?;%lj2tR?K@ zmLudCv+iq#pz7ZD%cNQ*bI4R_7ph9YE6NckY(P~T5%wVL1qkb@BvUa!`?1H3proW_ zG#7n!iF>l2w&IuR{njF~p8dOZr9RFvx3-;0E#Vlv?>q4lLbe_Q*X&~T?P6RJ{I3zFD+#z{^<7peio9;$X)Uxov4x1%`p4cF|1)i&E04IG7fg+S4 zlp|CiK%~Rx37a`o9IxT;4$Y^&(oM42*R#na*DSwV+<{>iQ{j+0WF)tY*DMsQOczt> zxG)!RMMO(B-z^xvr%|bXbrn6>L$Mx`%Uxd;;hT|G+!FrP?D`beS zR}Gt|Gd!k{VMa|OVCQ;DinEU!;T_*1s&Nxhjg`we zA=4Q$Zsukdvd?;|3X0n4o8T+rKb}5)I%bqcCAfsRWv`v?Vy50z`ZMT9^=wOTTfS2t zDQIH`Dd_RrH#qD(DuXrbZf~CF?K=-{-FSHM2ajhiFMN0zs=^nyFF%=_TsZ&fqf4JW zoIG7;A2SEwx1p1M>ajyX4B{jrw{4=(2_GV;Sot<6lJ+Kx-bm95Cu_wJ-CUPkc6$4am>7*zRQ63fvNrUHx7f1^$ zQ3@VXJh@<&2a zk@8p6S@1DqBv!SVnAJ97B^G$L!Ea~y4E-=-C0QB+u`SNDsiX7Q?xU|v)Zr${z7EdA zfQTFEp|SXD9QKtJ(gs}958%{${ewXoAOK#b1VKQxjqfLMsA(WYczp4g~x{p&o=`1ibT6yxdT%#T2UvokTd#8eHA~0TKj3 A5dZ)H delta 4999 zcmbtYYfxLq71mxo5R#CEF-E+ELgE@Q4;x|#wjqFdnU~Ge0)sCgUEmsVrS84LHWiTA zNhh%zmw2b1Op>NBZZpkvrX8Xm{n5#gWb&gko$2d`+ulEJ)4J3CX{Xape{?$2?pevY zl8|b*)r`K`$JyO;zCC-+kv{qqy}gI*<2gAN9sRU^b9brhyP7R!RduZXfNN{jqbKvwu=;W{4-_(kt^HK#qK1loPR3(;Ca+0fO?V~VI z0Z<80MM1S;K@Ry99t(56>$n=mIe;2+&sO$KEi{hU>~;@#dm2X0eiWF znb{44JpiuAj(vkG(~M`M?J(5=&<0RVT62%^ut+t}&&h%`w<6#gn9-t9M(TREb)l48ccF7juzs11Zt?9i-CvB3sSp{_p06h2xwW^I9uUIcJOzFhbl)S)T-6zsFZYhZQ!wc+vDWBRCgATx^-bj@e< ztFdUn6I7{Da;v02NvU6#-2X3?V)`T#h(}{e{mS(puJ$5T=(j0KU1UtXQ+7Vpn;~Xa zCVDdqqOOR;y@z@<64|K6WE)-ew6`8-vZ}l#^M!iI%jJ2;MhsnM_!jiS)iluqU{O*p0Es@->GPFf7`s{)qAfV9s!sSFu& zI_UFiJnCP3a+Fo|qNL!GM}V3}l5YHR(+`l_4d?d)EC58wScK*!fODj|xdAPcmFAZR{*Nb+ z;vDgKJalR-v-hr31^xooBs^*(US}8o-06uVTV6eVj?3L=26@<0hNj5BT8hyUDQ@jD zMQ@!fwC*L;8c%k0)if6j3X(6Owt|qU8mZ z6~Mi&tC>CS20iyOunJm4(}>-OZ^T~Eog|O@Yj>;uNv|hBhWkn=DNPdbRL4%c?zlt%L*2Q*bgHs09r^o zfb&y-8iBxB^8Eq$Y5H)W1T~U>4m@K@a?GZ|o0%zmg>LX(GKDl+zd~JejI<7kNm=;A zp}Kq)Yrg`gz5&3l-xc}$(5?Z6$hSuut$i@O39w53IQkvb8~NJUEZSsC7%8mYW7}K> zscQh>KnWYNjCK>o+5tKMG#XhlYlE?B02a1cMbr;njV(KS*s4ty@M?|d6qlQ`7ZSduW(L1`Yb2|v>=nC6*j_+XDvcn;rR24SGWzAI9 z>dOn8Q)I1SGn4&k?C{THr`p3@RB~FsZdf;x>Zy_xl^UWdak!JIafmLYOMLlE9Ydlz zzW5wDHC>whxObS$PM4J)T()d+_*0}?w<6tIm68{>-bCv*rfvawce>8t9Kv5EKbg*_ zb-<(PXH9p2#a?2c87xpu(Qv+ImP5XPyJpnQe3JZWW`rzVZdg2o*{zsql8~>8$`WQb zkm8@r4WGgTne(yRV}8sWH|mBYo3EJq>KrWP`7Ap@a0l z7bpE%kPSJIgJY;SBPYy|-|=VqJ7F7399+<1HRWB}5@D;y#Tfk$2gWLk@c;<i}N>7z2PGg&2N>VGqD0z&rrde;8^T3?%`E T+=p)fK(N8L0q&AVvz~tgtAnun diff --git a/app.log b/app.log index 729e27e..895d89f 100644 --- a/app.log +++ b/app.log @@ -115,3 +115,62 @@ 2025-08-29 21:18:18.748 | INFO | threads:run:907 - 服务文件存在: -rw-r--r-- 1 xiaji xiaji 703 Aug 28 22:27 /etc/systemd/system/gunicorn_django.service 2025-08-29 21:18:18.818 | ERROR | threads:run:925 - 服务状态查询失败: [sudo] password for xiaji: 2025-08-29 21:18:18.819 | ERROR | gunicorn_tab:on_manage_service_result:476 - 服务操作失败: [sudo] password for xiaji: +2025-08-29 21:22:59.668 | INFO | __main__::109 - 应用程序启动 +2025-08-29 21:22:59.708 | INFO | remote_command_tab:load_git_config:109 - 从配置文件加载git配置: git_url=http://192.168.3.241:3000/xiaji/webstatus, project_path=/home/xiaji/ +2025-08-29 21:22:59.710 | INFO | django_tab:load_django_path:116 - 从当前服务器配置加载Django路径: /home/xiaji/ +2025-08-29 21:22:59.710 | INFO | django_tab:__init__:22 - Django标签已连接到服务器切换信号 +2025-08-29 21:22:59.712 | INFO | gunicorn_tab:load_gunicorn_config:191 - 从当前服务器配置加载Gunicorn配置: remote_directory=/home/xiaji/, project_name=statuspage +2025-08-29 21:22:59.712 | INFO | gunicorn_tab:__init__:50 - Gunicorn标签已连接到服务器切换信号 +2025-08-29 21:23:01.913 | INFO | threads:run:47 - SSH连接成功: 192.168.3.157 +2025-08-29 21:23:08.980 | INFO | gunicorn_tab:manage_service:451 - 正在执行服务 status 操作,服务名称: gunicorn_django +2025-08-29 21:23:08.984 | INFO | gunicorn_tab:get_password:313 - 密码为空,弹出密码输入对话框 +2025-08-29 21:23:12.876 | INFO | gunicorn_tab:get_password:317 - 从对话框获取密码,长度: 5 +2025-08-29 21:23:12.877 | INFO | gunicorn_tab:manage_service:468 - 获取到密码,长度: 5,创建ManageGunicornServiceThread线程 +2025-08-29 21:23:12.878 | INFO | threads:run:893 - 执行服务管理命令: systemctl status gunicorn_django +2025-08-29 21:23:12.886 | INFO | threads:run:907 - 服务文件存在: -rw-r--r-- 1 xiaji xiaji 703 Aug 28 22:27 /etc/systemd/system/gunicorn_django.service +2025-08-29 21:23:12.955 | INFO | threads:run:917 - 服务状态查询命令执行完成,退出状态: 3 +2025-08-29 21:23:12.956 | ERROR | threads:run:928 - 服务状态查询失败: [sudo] password for xiaji: +2025-08-29 21:23:12.957 | ERROR | gunicorn_tab:on_manage_service_result:483 - 服务操作失败: [sudo] password for xiaji: +2025-08-29 21:23:12.957 | WARNING | gunicorn_tab:on_manage_service_result:487 - 检测到可能的密码问题,提示用户重新输入密码 +2025-08-29 21:24:05.472 | INFO | __main__::109 - 应用程序启动 +2025-08-29 21:24:05.509 | INFO | remote_command_tab:load_git_config:109 - 从配置文件加载git配置: git_url=http://192.168.3.241:3000/xiaji/webstatus, project_path=/home/xiaji/ +2025-08-29 21:24:05.510 | INFO | django_tab:load_django_path:116 - 从当前服务器配置加载Django路径: /home/xiaji/ +2025-08-29 21:24:05.511 | INFO | django_tab:__init__:22 - Django标签已连接到服务器切换信号 +2025-08-29 21:24:05.512 | INFO | gunicorn_tab:load_gunicorn_config:191 - 从当前服务器配置加载Gunicorn配置: remote_directory=/home/xiaji/, project_name=statuspage +2025-08-29 21:24:05.512 | INFO | gunicorn_tab:__init__:50 - Gunicorn标签已连接到服务器切换信号 +2025-08-29 21:24:07.760 | INFO | threads:run:47 - SSH连接成功: 192.168.3.157 +2025-08-29 21:24:09.577 | INFO | gunicorn_tab:manage_service:451 - 正在执行服务 status 操作,服务名称: gunicorn_django +2025-08-29 21:24:09.582 | INFO | gunicorn_tab:get_password:313 - 密码为空,弹出密码输入对话框 +2025-08-29 21:24:12.781 | INFO | gunicorn_tab:get_password:317 - 从对话框获取密码,长度: 5 +2025-08-29 21:24:12.781 | INFO | gunicorn_tab:manage_service:468 - 获取到密码,长度: 5,创建ManageGunicornServiceThread线程 +2025-08-29 21:24:12.783 | INFO | threads:run:893 - 执行服务管理命令: systemctl status gunicorn_django +2025-08-29 21:24:12.791 | INFO | threads:run:907 - 服务文件存在: -rw-r--r-- 1 xiaji xiaji 703 Aug 28 22:27 /etc/systemd/system/gunicorn_django.service +2025-08-29 21:24:12.862 | INFO | threads:run:918 - 服务状态查询命令执行完成,退出状态: 0 +2025-08-29 21:24:12.863 | INFO | threads:run:933 - 服务状态查询成功: gunicorn_django +2025-08-29 21:24:12.866 | INFO | gunicorn_tab:on_manage_service_result:480 - 服务操作成功: +2025-08-29 21:26:22.602 | INFO | __main__::109 - 应用程序启动 +2025-08-29 21:26:22.655 | INFO | remote_command_tab:load_git_config:109 - 从配置文件加载git配置: git_url=http://192.168.3.241:3000/xiaji/webstatus, project_path=/home/xiaji/ +2025-08-29 21:26:22.656 | INFO | django_tab:load_django_path:116 - 从当前服务器配置加载Django路径: /home/xiaji/ +2025-08-29 21:26:22.656 | INFO | django_tab:__init__:22 - Django标签已连接到服务器切换信号 +2025-08-29 21:26:22.657 | INFO | gunicorn_tab:load_gunicorn_config:191 - 从当前服务器配置加载Gunicorn配置: remote_directory=/home/xiaji/, project_name=statuspage +2025-08-29 21:26:22.658 | INFO | gunicorn_tab:__init__:50 - Gunicorn标签已连接到服务器切换信号 +2025-08-29 21:26:24.338 | INFO | threads:run:47 - SSH连接成功: 192.168.3.157 +2025-08-29 21:27:33.792 | INFO | __main__::109 - 应用程序启动 +2025-08-29 21:27:33.828 | INFO | remote_command_tab:load_git_config:109 - 从配置文件加载git配置: git_url=http://192.168.3.241:3000/xiaji/webstatus, project_path=/home/xiaji/ +2025-08-29 21:27:33.829 | INFO | django_tab:load_django_path:116 - 从当前服务器配置加载Django路径: /home/xiaji/ +2025-08-29 21:27:33.830 | INFO | django_tab:__init__:22 - Django标签已连接到服务器切换信号 +2025-08-29 21:27:33.831 | INFO | gunicorn_tab:load_gunicorn_config:191 - 从当前服务器配置加载Gunicorn配置: remote_directory=/home/xiaji/, project_name=statuspage +2025-08-29 21:27:33.832 | INFO | gunicorn_tab:__init__:50 - Gunicorn标签已连接到服务器切换信号 +2025-08-29 21:27:36.356 | INFO | threads:run:47 - SSH连接成功: 192.168.3.157 +2025-08-29 21:27:40.666 | INFO | threads:run:705 - 找到wsgi.py文件: /home/xiaji/statuspage/wsgi.py +2025-08-29 21:27:43.798 | INFO | threads:run:736 - Gunicorn测试成功 - 项目: statuspage +2025-08-29 21:27:43.799 | INFO | gunicorn_tab:on_test_gunicorn_result:385 - Gunicorn测试成功: Gunicorn测试成功 - 项目: statuspage +2025-08-29 21:27:54.236 | INFO | threads:run:397 - Django测试服务器启动成功 +2025-08-29 21:27:54.238 | INFO | django_tab:on_test_django_result:193 - Django测试启动成功: Django测试服务器启动成功 +2025-08-29 21:28:12.058 | INFO | threads:run:705 - 找到wsgi.py文件: /home/xiaji/statuspage/wsgi.py +2025-08-29 21:28:15.177 | INFO | threads:run:736 - Gunicorn测试成功 - 项目: statuspage +2025-08-29 21:28:15.179 | INFO | gunicorn_tab:on_test_gunicorn_result:385 - Gunicorn测试成功: Gunicorn测试成功 - 项目: statuspage +2025-08-29 21:28:21.359 | INFO | gunicorn_tab:get_password:313 - 密码为空,弹出密码输入对话框 +2025-08-29 21:28:25.808 | INFO | gunicorn_tab:get_password:317 - 从对话框获取密码,长度: 5 +2025-08-29 21:28:27.972 | INFO | gunicorn_tab:get_password:313 - 密码为空,弹出密码输入对话框 +2025-08-29 21:28:31.580 | INFO | gunicorn_tab:get_password:317 - 从对话框获取密码,长度: 5 diff --git a/gunicorn_tab.py b/gunicorn_tab.py index 253d952..54193d2 100644 --- a/gunicorn_tab.py +++ b/gunicorn_tab.py @@ -223,7 +223,8 @@ class GunicornTab(QWidget): """根据config.json配置生成服务文件内容""" username = config.get('username', 'www-data') project_name = config.get('project_name', 'myproject') - django_path = config.get('remote_directory', '/home/user') + remote_directory = config.get('remote_directory', '/home/user') + django_path = config.get('django_path', remote_directory) # 构建完整的项目路径 project_path = f"{django_path.rstrip('/')}" @@ -238,8 +239,8 @@ Group={username} WorkingDirectory={project_path} # 所有Gunicorn参数直接在这里配置 ExecStart=/usr/local/bin/gunicorn \\ - --bind 127.0.0.1:8000 \\ - --workers $(nproc --all * 2 + 1) \\ + --bind 0.0.0.0:8000 \\ + --workers 3 \\ --worker-class sync \\ --timeout 60 \\ --name {project_name} \\ @@ -306,16 +307,20 @@ WantedBy=multi-user.target""" password = None if self.parent and hasattr(self.parent, 'server_connection_tab'): password = self.parent.server_connection_tab.password_input.text() + logger.info(f"从server_connection_tab获取密码,长度: {len(password) if password else 0}") # 如果密码为空,弹出密码输入对话框 if not password: + logger.info("密码为空,弹出密码输入对话框") dialog = PasswordDialog(self) if dialog.exec_() == QDialog.Accepted: password = dialog.get_password() + logger.info(f"从对话框获取密码,长度: {len(password) if password else 0}") # 保存密码到服务器连接标签页 if self.parent and hasattr(self.parent, 'server_connection_tab'): self.parent.server_connection_tab.password_input.setText(password) else: + logger.warning("用户取消了密码输入") return None return password @@ -355,6 +360,7 @@ WantedBy=multi-user.target""" logger.error(f"Gunicorn安装失败: {message}") def test_gunicorn(self): + """测试Gunicorn配置""" if not self.check_ssh_connection(): return @@ -362,16 +368,26 @@ WantedBy=multi-user.target""" if not django_path: QMessageBox.warning(self, "警告", "请输入Django项目路径") return + + # 获取端口配置 + port = self.port_input.text().strip() or "8000" - self.output_text.append(f"正在测试Gunicorn {django_path}...") + # 记录测试参数 + logger.info(f"开始测试Gunicorn,Django路径: {django_path}, 端口: {port}") + + self.output_text.append(f"正在测试Gunicorn {django_path} (端口: {port})...") self.test_gunicorn_btn.setEnabled(False) self.progress_bar.setVisible(True) self.progress_bar.setValue(0) - self.gunicorn_test_thread = GunicornTestThread(self.parent.ssh_client, django_path) + logger.info("创建Gunicorn测试线程") + # 注意:GunicornTestThread构造函数可能需要更新以接受端口参数 + # 确保测试时使用的端口与实际服务启动时一致 + self.gunicorn_test_thread = GunicornTestThread(self.parent.ssh_client, django_path, port) self.gunicorn_test_thread.progress_updated.connect(self.update_progress) self.gunicorn_test_thread.result_ready.connect(self.on_test_gunicorn_result) self.gunicorn_test_thread.start() + logger.info("Gunicorn测试线程已启动") def on_test_gunicorn_result(self, success, message): self.test_gunicorn_btn.setEnabled(True) @@ -444,6 +460,7 @@ WantedBy=multi-user.target""" # 使用gunicorn_[project_name].service格式作为服务名称 service_name = f"gunicorn_{project_name}" + logger.info(f"正在执行服务 {action} 操作,服务名称: {service_name}") self.output_text.append(f"正在执行服务 {action} 操作...") # 禁用所有服务管理按钮 @@ -455,13 +472,20 @@ WantedBy=multi-user.target""" # 获取密码 password = self.get_password() if password is None: + logger.warning("未获取到密码,取消服务操作") for btn in buttons: btn.setEnabled(True) return - self.manage_thread = ManageGunicornServiceThread(self.parent.ssh_client, service_name, action, password) + # 获取端口配置 + port = self.port_input.text().strip() or "8000" + + logger.info(f"获取到密码,长度: {len(password)},创建ManageGunicornServiceThread线程") + logger.info(f"开始管理Gunicorn服务 - 服务名: {service_name}, 操作: {action}, 端口: {port}") + self.manage_thread = ManageGunicornServiceThread(self.parent.ssh_client, service_name, action, password, port) self.manage_thread.result_ready.connect(lambda s, m: self.on_manage_service_result(s, m, buttons)) self.manage_thread.start() + logger.info(f"Gunicorn服务管理线程已启动 - 操作: {action}") def on_manage_service_result(self, success, message, buttons): # 重新启用所有服务管理按钮 @@ -474,6 +498,10 @@ WantedBy=multi-user.target""" else: self.output_text.append(f"服务操作失败: {message}") logger.error(f"服务操作失败: {message}") + # 如果是密码相关错误,提供更详细的提示 + if "password" in message.lower() or "sudo" in message.lower(): + self.output_text.append("提示:请检查sudo密码是否正确,或尝试重新输入密码") + logger.warning("检测到可能的密码问题,提示用户重新输入密码") def on_server_changed(self): self.load_gunicorn_config() diff --git a/threads.py b/threads.py index 161946b..c692706 100644 --- a/threads.py +++ b/threads.py @@ -617,8 +617,16 @@ class GunicornInstallThread(QThread): self.progress_updated.emit(10) # 检查Gunicorn是否已安装 + logger.info("检查Gunicorn是否已安装") stdin, stdout, stderr = self.ssh_client.exec_command("gunicorn --version") + exit_status = stdout.channel.recv_exit_status() gunicorn_version = stdout.read().decode().strip() + version_error = stderr.read().decode() + + logger.info(f"Gunicorn版本检查状态: {exit_status}") + logger.info(f"Gunicorn版本信息: {gunicorn_version}") + if version_error: + logger.error(f"Gunicorn版本检查错误: {version_error}") if gunicorn_version: self.result_ready.emit(True, f"Gunicorn已安装: {gunicorn_version}") @@ -628,34 +636,80 @@ class GunicornInstallThread(QThread): self.progress_updated.emit(30) # 尝试使用pip安装 + logger.info("开始使用pip安装Gunicorn") stdin, stdout, stderr = self.ssh_client.exec_command("pip3 install gunicorn") exit_status = stdout.channel.recv_exit_status() + install_output = stdout.read().decode() + install_error = stderr.read().decode() + + logger.info(f"Gunicorn pip安装命令执行状态: {exit_status}") + logger.info(f"Gunicorn pip安装输出: {install_output}") + if install_error: + logger.error(f"Gunicorn pip安装错误: {install_error}") if exit_status == 0: self.progress_updated.emit(90) + # 验证安装 + logger.info("验证Gunicorn安装") stdin, stdout, stderr = self.ssh_client.exec_command("gunicorn --version") + version_exit_status = stdout.channel.recv_exit_status() gunicorn_version = stdout.read().decode().strip() - self.result_ready.emit(True, f"Gunicorn安装成功: {gunicorn_version}") - logger.info(f"Gunicorn安装成功: {gunicorn_version}") + version_error = stderr.read().decode() + + logger.info(f"Gunicorn版本检查状态: {version_exit_status}") + logger.info(f"Gunicorn版本信息: {gunicorn_version}") + if version_error: + logger.error(f"Gunicorn版本检查错误: {version_error}") + + if gunicorn_version: + self.result_ready.emit(True, f"Gunicorn安装成功: {gunicorn_version}") + logger.info(f"Gunicorn安装成功: {gunicorn_version}") + else: + self.result_ready.emit(False, "Gunicorn安装后无法获取版本信息") + logger.error("Gunicorn安装后无法获取版本信息") return self.progress_updated.emit(50) # 如果pip安装失败,尝试使用apt安装 + logger.info("pip安装失败,尝试使用apt安装Gunicorn") if self.password: + logger.info("使用密码进行sudo apt安装") stdin, stdout, stderr = self.ssh_client.exec_command("sudo -S apt update && sudo -S apt install -y gunicorn") stdin.write(f"{self.password}\n") stdin.flush() else: + logger.info("无密码进行sudo apt安装") stdin, stdout, stderr = self.ssh_client.exec_command("sudo apt update && sudo apt install -y gunicorn") exit_status = stdout.channel.recv_exit_status() + apt_output = stdout.read().decode() + apt_error = stderr.read().decode() + + logger.info(f"Gunicorn apt安装命令执行状态: {exit_status}") + logger.info(f"Gunicorn apt安装输出: {apt_output}") + if apt_error: + logger.error(f"Gunicorn apt安装错误: {apt_error}") if exit_status == 0: self.progress_updated.emit(90) + # 验证安装 + logger.info("验证apt安装的Gunicorn") stdin, stdout, stderr = self.ssh_client.exec_command("gunicorn --version") + version_exit_status = stdout.channel.recv_exit_status() gunicorn_version = stdout.read().decode().strip() - self.result_ready.emit(True, f"Gunicorn安装成功: {gunicorn_version}") - logger.info(f"Gunicorn安装成功: {gunicorn_version}") + version_error = stderr.read().decode() + + logger.info(f"Gunicorn版本检查状态: {version_exit_status}") + logger.info(f"Gunicorn版本信息: {gunicorn_version}") + if version_error: + logger.error(f"Gunicorn版本检查错误: {version_error}") + + if gunicorn_version: + self.result_ready.emit(True, f"Gunicorn安装成功: {gunicorn_version}") + logger.info(f"Gunicorn安装成功: {gunicorn_version}") + else: + self.result_ready.emit(False, "Gunicorn安装后无法获取版本信息") + logger.error("Gunicorn安装后无法获取版本信息") else: error = stderr.read().decode() self.result_ready.emit(False, f"Gunicorn安装失败: {error}") @@ -671,10 +725,12 @@ class GunicornTestThread(QThread): result_ready = Signal(bool, str) progress_updated = Signal(int) - def __init__(self, ssh_client, django_path): + def __init__(self, ssh_client, django_path, port="8000"): super().__init__() self.ssh_client = ssh_client self.django_path = django_path + self.port = port + logger.info(f"GunicornTestThread初始化 - Django路径: {django_path}, 端口: {port}") def run(self): try: @@ -716,38 +772,45 @@ class GunicornTestThread(QThread): # wsgi.py在项目根目录中,使用manage.py所在目录名作为项目名 project_name = os.path.basename(self.django_path.rstrip('/')) + logger.info(f"使用项目名: {project_name}, 工作目录: {self.django_path}") + self.progress_updated.emit(50) - # 测试Gunicorn启动 - test_command = f"cd {self.django_path} && timeout 10 gunicorn --workers 1 --bind 0.0.0.0:8001 {project_name}.wsgi:application --timeout 5" + # 测试Gunicorn启动,使用构造函数中传入的端口参数 + test_command = f"cd {self.django_path} && timeout 10 gunicorn --workers 1 --bind 0.0.0.0:{self.port} {project_name}.wsgi:application --timeout 5" + logger.info(f"执行Gunicorn测试命令: {test_command}") stdin, stdout, stderr = self.ssh_client.exec_command(test_command) time.sleep(3) # 等待Gunicorn启动 # 检查Gunicorn是否运行 stdin, stdout, stderr = self.ssh_client.exec_command("ps aux | grep gunicorn") output = stdout.read().decode() + logger.info(f"Gunicorn进程检查结果: {output}") # 清理测试进程 - stdin, stdout, stderr = self.ssh_client.exec_command("pkill -f 'gunicorn.*8001'") + stdin, stdout, stderr = self.ssh_client.exec_command(f"pkill -f 'gunicorn.*{self.port}'") - if "gunicorn" in output and "8001" in output: + if "gunicorn" in output and f":{self.port}" in output: self.progress_updated.emit(100) - self.result_ready.emit(True, f"Gunicorn测试成功 - 项目: {project_name}") - logger.info(f"Gunicorn测试成功 - 项目: {project_name}") + self.result_ready.emit(True, f"Gunicorn测试成功 - 项目: {project_name}, 端口: {self.port}") + logger.info(f"Gunicorn测试成功 - 项目: {project_name}, 端口: {self.port}") else: # 尝试更简单的测试方式 self.progress_updated.emit(80) test_command2 = f"cd {self.django_path} && python3 -c \"import {project_name}.wsgi; print('WSGI导入成功')\"" + logger.info(f"执行WSGI导入测试: {test_command2}") stdin, stdout, stderr = self.ssh_client.exec_command(test_command2) exit_status = stdout.channel.recv_exit_status() if exit_status == 0: + wsgi_output = stdout.read().decode() + logger.info(f"WSGI导入测试成功: {wsgi_output}") self.result_ready.emit(True, f"WSGI配置验证成功 - 项目: {project_name}") logger.info(f"WSGI配置验证成功 - 项目: {project_name}") else: error_output = stderr.read().decode() - self.result_ready.emit(False, f"Gunicorn测试失败: {error_output}") logger.error(f"Gunicorn测试失败: {error_output}") + self.result_ready.emit(False, f"Gunicorn测试失败: {error_output}") except Exception as e: error_msg = str(e) @@ -873,12 +936,14 @@ class UploadGunicornServiceThread(QThread): class ManageGunicornServiceThread(QThread): result_ready = Signal(bool, str) - def __init__(self, ssh_client, service_name, action, password=None): + def __init__(self, ssh_client, service_name, action, password=None, port="8000"): super().__init__() self.ssh_client = ssh_client self.service_name = service_name self.action = action self.password = password + self.port = port + logger.info(f"ManageGunicornServiceThread初始化 - 服务名: {service_name}, 操作: {action}, 端口: {port}") def run(self): try: @@ -910,32 +975,77 @@ class ManageGunicornServiceThread(QThread): # 状态命令需要特殊处理 if self.password: stdin, stdout, stderr = self.ssh_client.exec_command(f"sudo -S {cmd}") + # 立即写入密码并关闭stdin stdin.write(f"{self.password}\n") - stdin.flush() + stdin.close() + # 等待命令执行完成 + exit_status = stdout.channel.recv_exit_status() + logger.info(f"服务状态查询命令执行完成,退出状态: {exit_status}") + # 读取输出和错误 + output = stdout.read().decode() + error = stderr.read().decode() + # 检查是否仍然提示输入密码 + if "password for" in error.lower() and exit_status != 0: + logger.error(f"密码传递失败,仍然提示输入密码: {error}") + self.result_ready.emit(False, error) + return else: stdin, stdout, stderr = self.ssh_client.exec_command(f"sudo {cmd}") - exit_status = stdout.channel.recv_exit_status() + exit_status = stdout.channel.recv_exit_status() + # 读取输出和错误 + output = stdout.read().decode() + error = stderr.read().decode() if exit_status == 0: - output = stdout.read().decode() logger.info(f"服务状态查询成功: {service_name_for_systemd}") self.result_ready.emit(True, output) else: - error = stderr.read().decode() logger.error(f"服务状态查询失败: {error}") self.result_ready.emit(False, error) else: # 其他操作(start, stop, restart, enable, disable) if self.password: stdin, stdout, stderr = self.ssh_client.exec_command(f"sudo -S {cmd}") + # 立即写入密码并关闭stdin stdin.write(f"{self.password}\n") - stdin.flush() + stdin.close() + # 等待命令执行完成 + exit_status = stdout.channel.recv_exit_status() + logger.info(f"服务{self.action}命令执行完成,退出状态: {exit_status}") + # 读取错误输出 + error = stderr.read().decode() + # 检查是否仍然提示输入密码 + if "password for" in error.lower() and exit_status != 0: + logger.error(f"密码传递失败,仍然提示输入密码: {error}") + self.result_ready.emit(False, error) + return else: stdin, stdout, stderr = self.ssh_client.exec_command(f"sudo {cmd}") - exit_status = stdout.channel.recv_exit_status() + exit_status = stdout.channel.recv_exit_status() if exit_status == 0: logger.info(f"服务{self.action}成功: {service_name_for_systemd}") + + # 如果是启动操作,额外检查服务状态 + if self.action == "start": + time.sleep(2) # 等待服务完全启动 + stdin, stdout, stderr = self.ssh_client.exec_command(f"sudo systemctl status {service_name_for_systemd}") + status_exit_status = stdout.channel.recv_exit_status() + status_output = stdout.read().decode() + status_error = stderr.read().decode() + + if status_exit_status == 0: + logger.info(f"服务启动后状态检查成功: {status_output}") + # 检查端口是否被监听,从服务名称中提取端口 + port = "8000" # 默认端口 + if hasattr(self, 'port') and self.port: + port = self.port + stdin, stdout, stderr = self.ssh_client.exec_command(f"sudo netstat -tlnp | grep :{port}") + netstat_output = stdout.read().decode() + logger.info(f"端口{port}监听状态: {netstat_output}") + else: + logger.warning(f"服务启动后状态检查失败: {status_error}") + self.result_ready.emit(True, f"服务{self.action}成功: {service_name_for_systemd}") else: error = stderr.read().decode()