On quick scan of a few immediate potential concerns..
Field type/dimension mismatches between primary key and presumably a foreign key. Consider aligning the field type to one size or the other:
- location.id and users.location_id (bigint(20) vs int(11));
- users.id and user_perms.user_id and other presumably foreign keys (int(10) vs int(11))
Duplicate enum definition across several fields in different tables. Consider making this a seperate lookup table:
- users.type, roles.user_type, resources.user_type (internal, client, expert)
As a secondary note about naming, I would strongly consider naming these resources to closely match the domain. If the resource is a contractor, name it a contractor. Expert implies a high skill level, and when interpruted by a reasonable person who doesn't know the underlying meaning in the system, could take that to mean something much different, as you would certainly have internal experts, and client experts.)
Initial Auto_Increment values seem strange. Usually if I choose to not start incrementing at one (1), I would start at a much higher number ~ 100, 1000. There are reasons to assign random keys to prevent 'mining', but this isn't the mechanism. Is there are a particular reason for starting at 2, 3, or 4?
Similar issues for other fields migh exist. Those I listed caught my eye first.
EDIT:
Upon clarification of you question, I might suggest that 'it depends' as to whether to split the roles to seperate tables depending on site, and if so, how. Because they are all roles, and because you intend to provide cross subdomain site interaction/communication, they won't be completely distinct roles, and you probably would not necessarily need a distinct method of using or managing them. The term multi-tenancy applies here.
What you have for roles now is essentially a 'table per heirarchy' to represent different kinds of roles. Unless a certain site's concept of a role grows in complexity or diverges from the common role structure of the other sites to the point that the other sites that share the same physical table leave a lot of sparse records, you probably wouldn't want to change that fundamental structure of your table to represent essentially three site role classes (via 'user_type' discriminators of internal, client, and expert). If a role is a role is a role, regardless of the site, what you have is fine; otherwise, a 'table per type' is something you may want to look at.
Discussing the domain a little more, including reiterating what was said originally and in comments..
The user_type, might be described as 'site' 'membership', and each 'site' has their own set of rules about role assignment. There is no overwhelming reason to change your terminology, it's just clearer for me as I internalize it.
An 'internal' user is a member and has access/interaction within the 'internal' site. In fact all users have a 'home' or primary site, and one could refer to that relationship as a site membership. A user with primarily 'internal' membership might have additional limited membership to one or both of the other two sites. A user who is primarily an 'expert' or a contractor would have membership, to the 'expert' or contractor site, and possibly a limited membership to 'internal' site areas (and similarly, but conversely for the 'client' members having limited membership to a different set of internal functions). The limits to membership are based on some policy...
Some roles will probably have common functionality across 'subdomain' sites (like those users who have responsibility for personnel/HR, admin, payroll, managers, common subsystems such as authenticator or notifier or reminder, etc). It is likely that some users may have more than one role, and the extent of a role varies from site to site. As an example, a certain contractor may be responsible for certain aspects of payroll on the 'expert' site (like reporting their own hours, and signing off as verification for the hours of other contractors), and wouldn't have the same set or responsibilities on the 'internal' site.
Knowing which site a user or member primarily belonged to (or perhaps more specifically called 'home'), would determine certain eligibility to global/common roles, primary/home site roles, and to roles with neighboring sites where they have an additional, established, limited membership. The 'internal' site is a neighbor to both the 'client' and 'expert' sites, but the 'expert' and 'client' sites are not neighbors to each other. In other words, there is no direct link between the two, and all interaction between an 'expert' role and a 'client' role happens via some intermediary role within the 'internal' site by an internal role granted to a user with primarily internal membership. There is a certain, specific relationship between two sites, and a relationship of site and a role. Some roles would simply be invalid for certain users, even if they were some how assigned a role because their membership, or lack of membership, precludes the role from being applicible. Some roles would be invalid for certain sites, but other roles, perhaps like 'commentor,' or 'reviewer' might be valid for all sites.
It might make sense to model site to site membership relationships, and then define your roles based on a particular relationship, instead of simply which site they call 'home.' I believe the concept is somewhat of a rudimentary context aware/sensative role based access control (RBAC). I found another post from stackexchangethat asks about a situation similar in some respects to yours, and you may want to consider this drawing for further implementation ideas (internally linked with within that post).
In any case, I hope this was helpful. Implementing effective, managable RBAC can be messy.
Poking holes: what if the database schema is changed at same point later in time, and a column name changes, or the column is deleted completely? Lots of database system allow this. What will happen to your "fieldName" then?
For data integrity: you must make sure that every update or delete operation will for sure update your tracking table. That is best accomplished by triggers calling a stored procedure. You should make sure only those stored procedure has writing access to your tracking table, so noone else can write wrong values.
If you can live with a db vendor specific solution: most db systems have system tables where the schema information (table names, table ids, column names etc) is stored. You can check if it is possible to set a foreign key reference to such a system table. That would allow to replace the field name by a field ID if the database supports something like this.
Actually, if you need to track whole rows of the specific table including all columns (and not just a small subset of the columns), you should consider @sarfeast's suggestion. Read this article about the drawbacks of name-value-pair models.
Best Answer
It is not necessarily bad for the server to log a line per page request. However, there are few things to consider here. First of all, I see you are using an AUTO_INCREMENT as a key. This will result in some performance loss as you are writing records to it.
You could replace the id with a GUID field and generate GUID's as you write lines.
Another optimization that you could to is to make this asynchronously. So the user requests a page, which you serve to the user, and while you do, you fire off a request, event, or whatever is suitable for your server side technology and does not block sending back the request, to actually write your logging.
Another thing to consider is, do you really need the data in a database immediately? It might be sufficient to just write to a log file first and import the data into a database at a later point in time.