logclient.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package logclient
  15. import (
  16. "context"
  17. "fmt"
  18. "strings"
  19. "time"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/appctx"
  23. "yunion.io/x/pkg/util/stringutils"
  24. "yunion.io/x/pkg/util/timeutils"
  25. "yunion.io/x/pkg/utils"
  26. "yunion.io/x/sqlchemy"
  27. api "yunion.io/x/onecloud/pkg/apis/logger"
  28. "yunion.io/x/onecloud/pkg/appsrv"
  29. "yunion.io/x/onecloud/pkg/cloudcommon/consts"
  30. "yunion.io/x/onecloud/pkg/mcclient"
  31. "yunion.io/x/onecloud/pkg/mcclient/auth"
  32. "yunion.io/x/onecloud/pkg/mcclient/modules/logger"
  33. "yunion.io/x/onecloud/pkg/util/ctx"
  34. // "yunion.io/x/onecloud/pkg/mcclient/modules/websocket"
  35. )
  36. type SessionGenerator func(ctx context.Context, token mcclient.TokenCredential, region, apiVersion string) *mcclient.ClientSession
  37. var (
  38. DefaultSessionGenerator = auth.GetSession
  39. )
  40. // golang 不支持 const 的string array, http://t.cn/EzAvbw8
  41. var BLACK_LIST_OBJ_TYPE = []string{} // "parameter"}
  42. var logclientWorkerMan *appsrv.SWorkerManager
  43. func init() {
  44. logclientWorkerMan = appsrv.NewWorkerManager("LogClientWorkerManager", 1, 50, false)
  45. }
  46. type IObject interface {
  47. GetId() string
  48. GetName() string
  49. Keyword() string
  50. }
  51. type sSimpleObject struct {
  52. id string
  53. name string
  54. keyword string
  55. }
  56. func (s sSimpleObject) GetId() string {
  57. return s.id
  58. }
  59. func (s sSimpleObject) GetName() string {
  60. return s.name
  61. }
  62. func (s sSimpleObject) Keyword() string {
  63. return s.keyword
  64. }
  65. func NewSimpleObject(id, name, keyword string) IObject {
  66. return sSimpleObject{
  67. id: id,
  68. name: name,
  69. keyword: keyword,
  70. }
  71. }
  72. type IVirtualObject interface {
  73. IObject
  74. GetOwnerId() mcclient.IIdentityProvider
  75. }
  76. type IModule interface {
  77. Create(session *mcclient.ClientSession, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
  78. }
  79. type IStartable interface {
  80. GetStartTime() time.Time
  81. }
  82. // save log to db.
  83. func AddSimpleActionLog(model IObject, action string, iNotes interface{}, userCred mcclient.TokenCredential, success bool) {
  84. addLog(model, action, iNotes, userCred, success, time.Time{}, &logger.Actions, "", "")
  85. }
  86. func AddSimpleActionLog2(model IObject, action string, iNotes interface{}, userCred mcclient.TokenCredential, success bool, severity api.TEventSeverity, kind api.TEventKind) {
  87. addLog(model, action, iNotes, userCred, success, time.Time{}, &logger.Actions, severity, kind)
  88. }
  89. func AddActionLogWithContext(ctx context.Context, model IObject, action string, iNotes interface{}, userCred mcclient.TokenCredential, success bool) {
  90. addLog(model, action, iNotes, userCred, success, appctx.AppContextStartTime(ctx), &logger.Actions, "", "")
  91. }
  92. func AddActionLogWithContext2(ctx context.Context, model IObject, action string, iNotes interface{}, userCred mcclient.TokenCredential, success bool, severity api.TEventSeverity, kind api.TEventKind) {
  93. addLog(model, action, iNotes, userCred, success, appctx.AppContextStartTime(ctx), &logger.Actions, severity, kind)
  94. }
  95. func AddActionLogWithStartable(task IStartable, model IObject, action string, iNotes interface{}, userCred mcclient.TokenCredential, success bool) {
  96. addLog(model, action, iNotes, userCred, success, task.GetStartTime(), &logger.Actions, "", "")
  97. }
  98. func AddActionLogWithStartable2(task IStartable, model IObject, action string, iNotes interface{}, userCred mcclient.TokenCredential, success bool, severity api.TEventSeverity, kind api.TEventKind) {
  99. addLog(model, action, iNotes, userCred, success, task.GetStartTime(), &logger.Actions, severity, kind)
  100. }
  101. // add websocket log to notify active browser users
  102. // func PostWebsocketNotify(model IObject, action string, iNotes interface{}, userCred mcclient.TokenCredential, success bool) {
  103. // addLog(model, action, iNotes, userCred, success, time.Time{}, &websocket.Websockets)
  104. // }
  105. func addLog(model IObject, action string, iNotes interface{}, userCred mcclient.TokenCredential, success bool, startTime time.Time, module IModule, severity api.TEventSeverity, kind api.TEventKind) {
  106. // avoid log loop
  107. if !consts.OpsLogEnabled() && utils.IsInStringArray(action, []string{
  108. ACT_CREATE,
  109. }) {
  110. return
  111. }
  112. if ok, _ := utils.InStringArray(model.Keyword(), BLACK_LIST_OBJ_TYPE); ok {
  113. log.Errorf("不支持的 actionlog 类型")
  114. return
  115. }
  116. if action == ACT_UPDATE {
  117. if iNotes == nil {
  118. return
  119. }
  120. if uds, ok := iNotes.(sqlchemy.UpdateDiffs); ok && len(uds) == 0 {
  121. return
  122. }
  123. }
  124. notes := stringutils.Interface2String(iNotes)
  125. objId := model.GetId()
  126. if len(objId) == 0 {
  127. objId = "-"
  128. }
  129. objName := model.GetName()
  130. if len(objName) == 0 {
  131. objName = "-"
  132. }
  133. logentry := jsonutils.NewDict()
  134. logentry.Add(jsonutils.NewString(objName), "obj_name")
  135. logentry.Add(jsonutils.NewString(model.Keyword()), "obj_type")
  136. logentry.Add(jsonutils.NewString(objId), "obj_id")
  137. logentry.Add(jsonutils.NewString(action), "action")
  138. logentry.Add(jsonutils.NewString(userCred.GetUserId()), "user_id")
  139. logentry.Add(jsonutils.NewString(userCred.GetUserName()), "user")
  140. logentry.Add(jsonutils.NewString(userCred.GetTenantId()), "tenant_id")
  141. logentry.Add(jsonutils.NewString(userCred.GetTenantName()), "tenant")
  142. logentry.Add(jsonutils.NewString(userCred.GetDomainId()), "domain_id")
  143. logentry.Add(jsonutils.NewString(userCred.GetDomainName()), "domain")
  144. logentry.Add(jsonutils.NewString(userCred.GetProjectDomainId()), "project_domain_id")
  145. logentry.Add(jsonutils.NewString(userCred.GetProjectDomain()), "project_domain")
  146. logentry.Add(jsonutils.NewString(strings.Join(userCred.GetRoles(), ",")), "roles")
  147. logentry.Add(jsonutils.NewString(userCred.GetLoginIp()), "ip")
  148. logentry.Add(jsonutils.NewBool(userCred.IsSystemAccount()), "is_system_account")
  149. service := consts.GetServiceType()
  150. if len(service) > 0 {
  151. logentry.Add(jsonutils.NewString(service), "service")
  152. }
  153. if !startTime.IsZero() {
  154. logentry.Add(jsonutils.NewString(timeutils.FullIsoTime(startTime)), "start_time")
  155. }
  156. if virtualModel, ok := model.(IVirtualObject); ok {
  157. ownerId := virtualModel.GetOwnerId()
  158. if ownerId != nil {
  159. projectId := ownerId.GetProjectId()
  160. if len(projectId) > 0 {
  161. logentry.Add(jsonutils.NewString(projectId), "owner_tenant_id")
  162. }
  163. domainId := ownerId.GetProjectDomainId()
  164. if len(domainId) > 0 {
  165. logentry.Add(jsonutils.NewString(domainId), "owner_domain_id")
  166. }
  167. }
  168. }
  169. if !success {
  170. // 失败日志
  171. logentry.Add(jsonutils.JSONFalse, "success")
  172. if len(severity) == 0 {
  173. logentry.Add(jsonutils.NewString(string(api.SeverityError)), "severity")
  174. }
  175. } else {
  176. // 成功日志
  177. logentry.Add(jsonutils.JSONTrue, "success")
  178. if len(severity) == 0 {
  179. logentry.Add(jsonutils.NewString(string(api.SeverityInfo)), "severity")
  180. }
  181. }
  182. if len(severity) > 0 {
  183. logentry.Add(jsonutils.NewString(string(severity)), "severity")
  184. }
  185. if len(kind) > 0 {
  186. logentry.Add(jsonutils.NewString(string(kind)), "kind")
  187. }
  188. logentry.Add(jsonutils.NewString(notes), "notes")
  189. task := &logTask{
  190. userCred: userCred,
  191. api: module,
  192. logentry: logentry,
  193. }
  194. // keystone no need to auth
  195. // if auth.IsAuthed() {
  196. // task.userCred = auth.AdminCredential()
  197. // }
  198. logclientWorkerMan.Run(task, nil, nil)
  199. }
  200. type logTask struct {
  201. userCred mcclient.TokenCredential
  202. api IModule
  203. logentry *jsonutils.JSONDict
  204. }
  205. func (t *logTask) Run() {
  206. ctx := ctx.CtxWithTime()
  207. ctx = context.WithValue(ctx, appctx.APP_CONTEXT_KEY_APPNAME, consts.GetServiceType())
  208. s := DefaultSessionGenerator(ctx, t.userCred, "")
  209. _, err := t.api.Create(s, t.logentry)
  210. if err != nil {
  211. log.Errorf("create action log %s failed %s", t.logentry, err)
  212. }
  213. }
  214. func (t *logTask) Dump() string {
  215. return fmt.Sprintf("logTask %v %s", t.api, t.logentry)
  216. }